summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 07:32:08 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 07:32:08 +0000
commit328ad0a41c6bdf596224ff2e9ab9c0fabde8634d (patch)
tree973585a56cea8664b4be63b5bb737b443e4e2b76 /lib
parentInitial commit. (diff)
downloadnghttp3-upstream.tar.xz
nghttp3-upstream.zip
Adding upstream version 0.8.0.upstream/0.8.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--lib/CMakeLists.txt97
-rw-r--r--lib/Makefile.am97
-rw-r--r--lib/includes/CMakeLists.txt4
-rw-r--r--lib/includes/Makefile.am26
-rw-r--r--lib/includes/nghttp3/nghttp3.h2646
-rw-r--r--lib/includes/nghttp3/version.h.in46
-rw-r--r--lib/libnghttp3.pc.in34
-rw-r--r--lib/nghttp3_balloc.c91
-rw-r--r--lib/nghttp3_balloc.h92
-rw-r--r--lib/nghttp3_buf.c90
-rw-r--r--lib/nghttp3_buf.h74
-rw-r--r--lib/nghttp3_conn.c2532
-rw-r--r--lib/nghttp3_conn.h200
-rw-r--r--lib/nghttp3_conv.c125
-rw-r--r--lib/nghttp3_conv.h207
-rw-r--r--lib/nghttp3_debug.c61
-rw-r--r--lib/nghttp3_debug.h44
-rw-r--r--lib/nghttp3_err.c126
-rw-r--r--lib/nghttp3_err.h34
-rw-r--r--lib/nghttp3_frame.c204
-rw-r--r--lib/nghttp3_frame.h214
-rw-r--r--lib/nghttp3_gaptr.c169
-rw-r--r--lib/nghttp3_gaptr.h99
-rw-r--r--lib/nghttp3_http.c1654
-rw-r--r--lib/nghttp3_http.h202
-rw-r--r--lib/nghttp3_idtr.c80
-rw-r--r--lib/nghttp3_idtr.h90
-rw-r--r--lib/nghttp3_ksl.c827
-rw-r--r--lib/nghttp3_ksl.h348
-rw-r--r--lib/nghttp3_macro.h51
-rw-r--r--lib/nghttp3_map.c337
-rw-r--r--lib/nghttp3_map.h137
-rw-r--r--lib/nghttp3_mem.c124
-rw-r--r--lib/nghttp3_mem.h80
-rw-r--r--lib/nghttp3_objalloc.c41
-rw-r--r--lib/nghttp3_objalloc.h141
-rw-r--r--lib/nghttp3_opl.c47
-rw-r--r--lib/nghttp3_opl.h66
-rw-r--r--lib/nghttp3_pq.c168
-rw-r--r--lib/nghttp3_pq.h129
-rw-r--r--lib/nghttp3_qpack.c4093
-rw-r--r--lib/nghttp3_qpack.h996
-rw-r--r--lib/nghttp3_qpack_huffman.c122
-rw-r--r--lib/nghttp3_qpack_huffman.h108
-rw-r--r--lib/nghttp3_qpack_huffman_data.c4981
-rw-r--r--lib/nghttp3_range.c62
-rw-r--r--lib/nghttp3_range.h81
-rw-r--r--lib/nghttp3_rcbuf.c108
-rw-r--r--lib/nghttp3_rcbuf.h81
-rw-r--r--lib/nghttp3_ringbuf.c159
-rw-r--r--lib/nghttp3_ringbuf.h113
-rw-r--r--lib/nghttp3_str.c110
-rw-r--r--lib/nghttp3_str.h40
-rw-r--r--lib/nghttp3_stream.c1248
-rw-r--r--lib/nghttp3_stream.h396
-rw-r--r--lib/nghttp3_tnode.c97
-rw-r--r--lib/nghttp3_tnode.h66
-rw-r--r--lib/nghttp3_unreachable.c72
-rw-r--r--lib/nghttp3_unreachable.h47
-rw-r--r--lib/nghttp3_vec.c55
-rw-r--r--lib/nghttp3_vec.h41
-rw-r--r--lib/nghttp3_version.c39
62 files changed, 24949 insertions, 0 deletions
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
new file mode 100644
index 0000000..8022467
--- /dev/null
+++ b/lib/CMakeLists.txt
@@ -0,0 +1,97 @@
+# nghttp3
+#
+# Copyright (c) 2019 nghttp3
+# Copyright (c) 2016 ngtcp2
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+add_subdirectory(includes)
+
+include_directories(
+ "${CMAKE_CURRENT_SOURCE_DIR}/includes"
+ "${CMAKE_CURRENT_BINARY_DIR}/includes"
+)
+
+add_definitions(-DBUILDING_NGHTTP3)
+
+set(nghttp3_SOURCES
+ nghttp3_rcbuf.c
+ nghttp3_mem.c
+ nghttp3_str.c
+ nghttp3_conv.c
+ nghttp3_buf.c
+ nghttp3_ringbuf.c
+ nghttp3_pq.c
+ nghttp3_map.c
+ nghttp3_ksl.c
+ nghttp3_qpack.c
+ nghttp3_qpack_huffman.c
+ nghttp3_qpack_huffman_data.c
+ nghttp3_err.c
+ nghttp3_debug.c
+ nghttp3_conn.c
+ nghttp3_stream.c
+ nghttp3_frame.c
+ nghttp3_tnode.c
+ nghttp3_vec.c
+ nghttp3_gaptr.c
+ nghttp3_idtr.c
+ nghttp3_range.c
+ nghttp3_http.c
+ nghttp3_version.c
+ nghttp3_balloc.c
+ nghttp3_opl.c
+ nghttp3_objalloc.c
+ nghttp3_unreachable.c
+)
+
+# Public shared library
+if(ENABLE_SHARED_LIB)
+ add_library(nghttp3 SHARED ${nghttp3_SOURCES})
+ set_target_properties(nghttp3 PROPERTIES
+ COMPILE_FLAGS "${WARNCFLAGS}"
+ VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}
+ C_VISIBILITY_PRESET hidden
+ )
+
+ install(TARGETS nghttp3
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
+endif()
+
+if(HAVE_CUNIT OR ENABLE_STATIC_LIB)
+ # Static library (for unittests because of symbol visibility)
+ add_library(nghttp3_static STATIC ${nghttp3_SOURCES})
+ set_target_properties(nghttp3_static PROPERTIES
+ COMPILE_FLAGS "${WARNCFLAGS}"
+ VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}
+ ARCHIVE_OUTPUT_NAME nghttp3${STATIC_LIB_SUFFIX}
+ )
+ target_compile_definitions(nghttp3_static PUBLIC "-DNGHTTP3_STATICLIB")
+ if(ENABLE_STATIC_LIB)
+ install(TARGETS nghttp3_static
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+ endif()
+endif()
+
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnghttp3.pc"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..c9cc14b
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,97 @@
+# nghttp3
+#
+# Copyright (c) 2019 nghttp3
+# Copyright (c) 2016 ngtcp2
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+SUBDIRS = includes
+
+EXTRA_DIST = CMakeLists.txt
+
+AM_CFLAGS = $(WARNCFLAGS) $(DEBUGCFLAGS) $(EXTRACFLAG)
+AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGHTTP3
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libnghttp3.pc
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+lib_LTLIBRARIES = libnghttp3.la
+
+OBJECTS = \
+ nghttp3_rcbuf.c \
+ nghttp3_mem.c \
+ nghttp3_str.c \
+ nghttp3_conv.c \
+ nghttp3_buf.c \
+ nghttp3_ringbuf.c \
+ nghttp3_pq.c \
+ nghttp3_map.c \
+ nghttp3_ksl.c \
+ nghttp3_qpack.c \
+ nghttp3_qpack_huffman.c \
+ nghttp3_qpack_huffman_data.c \
+ nghttp3_err.c \
+ nghttp3_debug.c \
+ nghttp3_conn.c \
+ nghttp3_stream.c \
+ nghttp3_frame.c \
+ nghttp3_tnode.c \
+ nghttp3_vec.c \
+ nghttp3_gaptr.c \
+ nghttp3_idtr.c \
+ nghttp3_range.c \
+ nghttp3_http.c \
+ nghttp3_version.c \
+ nghttp3_balloc.c \
+ nghttp3_opl.c \
+ nghttp3_objalloc.c \
+ nghttp3_unreachable.c
+HFILES = \
+ nghttp3_rcbuf.h \
+ nghttp3_mem.h \
+ nghttp3_str.h \
+ nghttp3_conv.h \
+ nghttp3_buf.h \
+ nghttp3_ringbuf.h \
+ nghttp3_pq.h \
+ nghttp3_map.h \
+ nghttp3_ksl.h \
+ nghttp3_qpack.h \
+ nghttp3_qpack_huffman.h \
+ nghttp3_err.h \
+ nghttp3_debug.h \
+ nghttp3_conn.h \
+ nghttp3_stream.h \
+ nghttp3_frame.h \
+ nghttp3_tnode.h \
+ nghttp3_vec.h \
+ nghttp3_gaptr.h \
+ nghttp3_idtr.h \
+ nghttp3_range.h \
+ nghttp3_http.h \
+ nghttp3_balloc.h \
+ nghttp3_opl.h \
+ nghttp3_objalloc.h \
+ nghttp3_unreachable.h \
+ nghttp3_macro.h
+
+libnghttp3_la_SOURCES = $(HFILES) $(OBJECTS)
+libnghttp3_la_LDFLAGS = -no-undefined \
+ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
diff --git a/lib/includes/CMakeLists.txt b/lib/includes/CMakeLists.txt
new file mode 100644
index 0000000..76d7364
--- /dev/null
+++ b/lib/includes/CMakeLists.txt
@@ -0,0 +1,4 @@
+install(FILES
+ nghttp3/nghttp3.h
+ "${CMAKE_CURRENT_BINARY_DIR}/nghttp3/version.h"
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/nghttp3")
diff --git a/lib/includes/Makefile.am b/lib/includes/Makefile.am
new file mode 100644
index 0000000..b69d2fc
--- /dev/null
+++ b/lib/includes/Makefile.am
@@ -0,0 +1,26 @@
+# nghttp3
+#
+# Copyright (c) 2019 nghttp3 contributors
+# Copyright (c) 2016 ngtcp2 contributors
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+EXTRA_DIST = CMakeLists.txt
+
+nobase_include_HEADERS = nghttp3/nghttp3.h nghttp3/version.h
diff --git a/lib/includes/nghttp3/nghttp3.h b/lib/includes/nghttp3/nghttp3.h
new file mode 100644
index 0000000..7986ea8
--- /dev/null
+++ b/lib/includes/nghttp3/nghttp3.h
@@ -0,0 +1,2646 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2018 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2017 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_H
+#define NGHTTP3_H
+
+/* Define WIN32 when build target is Win32 API (borrowed from
+ libcurl) */
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+# define WIN32
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#if defined(_MSC_VER) && (_MSC_VER < 1800)
+/* MSVC < 2013 does not have inttypes.h because it is not C99
+ compliant. See compiler macros and version number in
+ https://sourceforge.net/p/predef/wiki/Compilers/ */
+# include <stdint.h>
+#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
+# include <inttypes.h>
+#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#include <nghttp3/version.h>
+
+#ifdef NGHTTP3_STATICLIB
+# define NGHTTP3_EXTERN
+#elif defined(WIN32)
+# ifdef BUILDING_NGHTTP3
+# define NGHTTP3_EXTERN __declspec(dllexport)
+# else /* !BUILDING_NGHTTP3 */
+# define NGHTTP3_EXTERN __declspec(dllimport)
+# endif /* !BUILDING_NGHTTP3 */
+#else /* !defined(WIN32) */
+# ifdef BUILDING_NGHTTP3
+# define NGHTTP3_EXTERN __attribute__((visibility("default")))
+# else /* !BUILDING_NGHTTP3 */
+# define NGHTTP3_EXTERN
+# endif /* !BUILDING_NGHTTP3 */
+#endif /* !defined(WIN32) */
+
+/**
+ * @typedef
+ *
+ * :type:`nghttp3_ssize` is signed counterpart of size_t.
+ */
+typedef ptrdiff_t nghttp3_ssize;
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ALPN_H3` is a serialized form of HTTP/3 ALPN
+ * protocol identifier this library supports. Notice that the first
+ * byte is the length of the following protocol identifier.
+ */
+#define NGHTTP3_ALPN_H3 "\x2h3"
+
+/**
+ * @macrosection
+ *
+ * nghttp3 library error codes
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` indicates that a passed
+ * argument is invalid.
+ */
+#define NGHTTP3_ERR_INVALID_ARGUMENT -101
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_NOBUF` indicates that a provided buffer does
+ * not have enough space to store data.
+ */
+#define NGHTTP3_ERR_NOBUF -102
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_STATE` indicates that a requested
+ * operation is not allowed at the current connection state.
+ */
+#define NGHTTP3_ERR_INVALID_STATE -103
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_WOULDBLOCK` indicates that an operation might
+ * block.
+ */
+#define NGHTTP3_ERR_WOULDBLOCK -104
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_STREAM_IN_USE` indicates that a stream ID is
+ * already in use.
+ */
+#define NGHTTP3_ERR_STREAM_IN_USE -105
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_MALFORMED_HTTP_HEADER` indicates that an HTTP
+ * header field is malformed.
+ */
+#define NGHTTP3_ERR_MALFORMED_HTTP_HEADER -107
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_REMOVE_HTTP_HEADER` indicates that an HTTP
+ * header field is discarded.
+ */
+#define NGHTTP3_ERR_REMOVE_HTTP_HEADER -108
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING` indicates that HTTP
+ * messaging is malformed.
+ */
+#define NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING -109
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL` indicates that a fatal error is
+ * occurred during QPACK processing and it cannot be recoverable.
+ */
+#define NGHTTP3_ERR_QPACK_FATAL -111
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE` indicates that a header
+ * field is too large to process.
+ */
+#define NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE -112
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` indicates that a stream is
+ * not found.
+ */
+#define NGHTTP3_ERR_STREAM_NOT_FOUND -114
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_CONN_CLOSING` indicates that a connection is
+ * closing state.
+ */
+#define NGHTTP3_ERR_CONN_CLOSING -116
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_STREAM_DATA_OVERFLOW` indicates that the length
+ * of stream data is too long and causes overflow.
+ */
+#define NGHTTP3_ERR_STREAM_DATA_OVERFLOW -117
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED` indicates that a
+ * QPACK decompression failed.
+ */
+#define NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED -402
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR` indicates that an
+ * error occurred while reading QPACK encoder stream.
+ */
+#define NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR -403
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR` indicates that an
+ * error occurred while reading QPACK decoder stream.
+ */
+#define NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR -404
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_FRAME_UNEXPECTED` indicates that an
+ * unexpected HTTP/3 frame is received.
+ */
+#define NGHTTP3_ERR_H3_FRAME_UNEXPECTED -408
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_FRAME_ERROR` indicates that an HTTP/3 frame
+ * is malformed.
+ */
+#define NGHTTP3_ERR_H3_FRAME_ERROR -409
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_MISSING_SETTINGS` indicates that an HTTP/3
+ * SETTINGS frame is missing.
+ */
+#define NGHTTP3_ERR_H3_MISSING_SETTINGS -665
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_INTERNAL_ERROR` indicates an internal error.
+ */
+#define NGHTTP3_ERR_H3_INTERNAL_ERROR -667
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM` indicates that a
+ * critical stream is closed.
+ */
+#define NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM -668
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR` indicates a general
+ * protocol error. This is typically a catch-all error.
+ */
+#define NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR -669
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_ID_ERROR` indicates that an ID related error
+ * occurred.
+ */
+#define NGHTTP3_ERR_H3_ID_ERROR -670
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_SETTINGS_ERROR` indicates that an HTTP/3
+ * SETTINGS frame is malformed.
+ */
+#define NGHTTP3_ERR_H3_SETTINGS_ERROR -671
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_H3_STREAM_CREATION_ERROR` indicates that a
+ * remote endpoint attempts to create a new stream which is not
+ * allowed.
+ */
+#define NGHTTP3_ERR_H3_STREAM_CREATION_ERROR -672
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_FATAL` indicates that error codes less than
+ * this value is fatal error. When this error is returned, an
+ * endpoint should drop connection immediately.
+ */
+#define NGHTTP3_ERR_FATAL -900
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM` indicates out of memory.
+ */
+#define NGHTTP3_ERR_NOMEM -901
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` indicates that user defined
+ * callback function failed.
+ */
+#define NGHTTP3_ERR_CALLBACK_FAILURE -902
+
+/**
+ * @macrosection
+ *
+ * HTTP/3 application error code
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_NO_ERROR` is HTTP/3 application error code
+ * ``H3_NO_ERROR``.
+ */
+#define NGHTTP3_H3_NO_ERROR 0x0100
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_GENERAL_PROTOCOL_ERROR` is HTTP/3 application
+ * error code ``H3_GENERAL_PROTOCOL_ERROR``.
+ */
+#define NGHTTP3_H3_GENERAL_PROTOCOL_ERROR 0x0101
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_INTERNAL_ERROR` is HTTP/3 application error code
+ * ``H3_INTERNAL_ERROR``.
+ */
+#define NGHTTP3_H3_INTERNAL_ERROR 0x0102
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_STREAM_CREATION_ERROR` is HTTP/3 application
+ * error code ``H3_STREAM_CREATION_ERROR``.
+ */
+#define NGHTTP3_H3_STREAM_CREATION_ERROR 0x0103
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_CLOSED_CRITICAL_STREAM` is HTTP/3 application
+ * error code ``H3_CLOSED_CRITICAL_STREAM``.
+ */
+#define NGHTTP3_H3_CLOSED_CRITICAL_STREAM 0x0104
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_FRAME_UNEXPECTED` is HTTP/3 application error
+ * code ``H3_FRAME_UNEXPECTED``.
+ */
+#define NGHTTP3_H3_FRAME_UNEXPECTED 0x0105
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_FRAME_ERROR` is HTTP/3 application error code
+ * ``H3_FRAME_ERROR``.
+ */
+#define NGHTTP3_H3_FRAME_ERROR 0x0106
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_EXCESSIVE_LOAD` is HTTP/3 application error code
+ * ``H3_EXCESSIVE_LOAD``.
+ */
+#define NGHTTP3_H3_EXCESSIVE_LOAD 0x0107
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_ID_ERROR` is HTTP/3 application error code
+ * ``H3_ID_ERROR``.
+ */
+#define NGHTTP3_H3_ID_ERROR 0x0108
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_SETTINGS_ERROR` is HTTP/3 application error code
+ * ``H3_SETTINGS_ERROR``.
+ */
+#define NGHTTP3_H3_SETTINGS_ERROR 0x0109
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_MISSING_SETTINGS` is HTTP/3 application error
+ * code ``H3_MISSING_SETTINGS``.
+ */
+#define NGHTTP3_H3_MISSING_SETTINGS 0x010a
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_REQUEST_REJECTED` is HTTP/3 application error
+ * code ``H3_REQUEST_REJECTED``.
+ */
+#define NGHTTP3_H3_REQUEST_REJECTED 0x010b
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_REQUEST_CANCELLED` is HTTP/3 application error
+ * code ``H3_REQUEST_CANCELLED``.
+ */
+#define NGHTTP3_H3_REQUEST_CANCELLED 0x010c
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_REQUEST_INCOMPLETE` is HTTP/3 application error
+ * code ``H3_REQUEST_INCOMPLETE``.
+ */
+#define NGHTTP3_H3_REQUEST_INCOMPLETE 0x010d
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_MESSAGE_ERROR` is HTTP/3 application error code
+ * ``H3_MESSAGE_ERROR``.
+ */
+#define NGHTTP3_H3_MESSAGE_ERROR 0x010e
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_CONNECT_ERROR` is HTTP/3 application error code
+ * ``H3_CONNECT_ERROR``.
+ */
+#define NGHTTP3_H3_CONNECT_ERROR 0x010f
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_H3_VERSION_FALLBACK` is HTTP/3 application error
+ * code ``H3_VERSION_FALLBACK``.
+ */
+#define NGHTTP3_H3_VERSION_FALLBACK 0x0110
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECOMPRESSION_FAILED` is HTTP/3 application
+ * error code ``QPACK_DECOMPRESSION_FAILED``.
+ */
+#define NGHTTP3_QPACK_DECOMPRESSION_FAILED 0x0200
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_ENCODER_STREAM_ERROR` is HTTP/3 application
+ * error code ``QPACK_ENCODER_STREAM_ERROR``.
+ */
+#define NGHTTP3_QPACK_ENCODER_STREAM_ERROR 0x0201
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODER_STREAM_ERROR` is HTTP/3 application
+ * error code ``QPACK_DECODER_STREAM_ERROR``.
+ */
+#define NGHTTP3_QPACK_DECODER_STREAM_ERROR 0x0202
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_malloc` is a custom memory allocator to replace
+ * :manpage:`malloc(3)`. The |user_data| is the
+ * :member:`nghttp3_mem.user_data`.
+ */
+typedef void *(*nghttp3_malloc)(size_t size, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_free` is a custom memory allocator to replace
+ * :manpage:`free(3)`. The |user_data| is the
+ * :member:`nghttp3_mem.user_data`.
+ */
+typedef void (*nghttp3_free)(void *ptr, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_calloc` is a custom memory allocator to replace
+ * :manpage:`calloc(3)`. The |user_data| is the
+ * :member:`nghttp3_mem.user_data`.
+ */
+typedef void *(*nghttp3_calloc)(size_t nmemb, size_t size, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_realloc` is a custom memory allocator to replace
+ * :manpage:`realloc(3)`. The |user_data| is the
+ * :member:`nghttp3_mem.user_data`.
+ */
+typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *user_data);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_mem` is a custom memory allocator functions and user
+ * defined pointer. The :member:`user_data` field is passed to each
+ * allocator function. This can be used, for example, to achieve
+ * per-session memory pool.
+ *
+ * In the following example code, ``my_malloc``, ``my_free``,
+ * ``my_calloc`` and ``my_realloc`` are the replacement of the
+ * standard allocators :manpage:`malloc(3)`, :manpage:`free(3)`,
+ * :manpage:`calloc(3)` and :manpage:`realloc(3)` respectively::
+ *
+ * void *my_malloc_cb(size_t size, void *user_data) {
+ * (void)user_data;
+ * return my_malloc(size);
+ * }
+ *
+ * void my_free_cb(void *ptr, void *user_data) {
+ * (void)user_data;
+ * my_free(ptr);
+ * }
+ *
+ * void *my_calloc_cb(size_t nmemb, size_t size, void *user_data) {
+ * (void)user_data;
+ * return my_calloc(nmemb, size);
+ * }
+ *
+ * void *my_realloc_cb(void *ptr, size_t size, void *user_data) {
+ * (void)user_data;
+ * return my_realloc(ptr, size);
+ * }
+ *
+ * void conn_new() {
+ * nghttp3_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb,
+ * my_realloc_cb};
+ *
+ * ...
+ * }
+ */
+typedef struct nghttp3_mem {
+ /**
+ * :member:`user_data` is an arbitrary user supplied data. This
+ * is passed to each allocator function.
+ */
+ void *user_data;
+ /**
+ * :member:`malloc` is a custom allocator function to replace
+ * :manpage:`malloc(3)`.
+ */
+ nghttp3_malloc malloc;
+ /**
+ * :member:`free` is a custom allocator function to replace
+ * :manpage:`free(3)`.
+ */
+ nghttp3_free free;
+ /**
+ * :member:`calloc` is a custom allocator function to replace
+ * :manpage:`calloc(3)`.
+ */
+ nghttp3_calloc calloc;
+ /**
+ * :member:`realloc` is a custom allocator function to replace
+ * :manpage:`realloc(3)`.
+ */
+ nghttp3_realloc realloc;
+} nghttp3_mem;
+
+/**
+ * @function
+ *
+ * `nghttp3_mem_default` returns the default memory allocator which
+ * uses malloc/calloc/realloc/free.
+ */
+NGHTTP3_EXTERN const nghttp3_mem *nghttp3_mem_default(void);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_vec` is ``struct iovec`` compatible structure to
+ * reference arbitrary array of bytes.
+ */
+typedef struct nghttp3_vec {
+ /**
+ * :member:`base` points to the data.
+ */
+ uint8_t *base;
+ /**
+ * :member:`len` is the number of bytes which the buffer pointed by
+ * base contains.
+ */
+ size_t len;
+} nghttp3_vec;
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_rcbuf` is the object representing reference counted
+ * buffer. The details of this structure are intentionally hidden
+ * from the public API.
+ */
+typedef struct nghttp3_rcbuf nghttp3_rcbuf;
+
+/**
+ * @function
+ *
+ * `nghttp3_rcbuf_incref` increments the reference count of |rcbuf| by
+ * 1.
+ */
+NGHTTP3_EXTERN void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf);
+
+/**
+ * @function
+ *
+ * `nghttp3_rcbuf_decref` decrements the reference count of |rcbuf| by
+ * 1. If the reference count becomes zero, the object pointed by
+ * |rcbuf| will be freed. In this case, application must not use
+ * |rcbuf| again.
+ */
+NGHTTP3_EXTERN void nghttp3_rcbuf_decref(nghttp3_rcbuf *rcbuf);
+
+/**
+ * @function
+ *
+ * `nghttp3_rcbuf_get_buf` returns the underlying buffer managed by
+ * |rcbuf|.
+ */
+NGHTTP3_EXTERN nghttp3_vec nghttp3_rcbuf_get_buf(const nghttp3_rcbuf *rcbuf);
+
+/**
+ * @function
+ *
+ * `nghttp3_rcbuf_is_static` returns nonzero if the underlying buffer
+ * is statically allocated, and 0 otherwise. This can be useful for
+ * language bindings that wish to avoid creating duplicate strings for
+ * these buffers.
+ */
+NGHTTP3_EXTERN int nghttp3_rcbuf_is_static(const nghttp3_rcbuf *rcbuf);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_buf` is the variable size buffer.
+ */
+typedef struct nghttp3_buf {
+ /**
+ * :member:`begin` points to the beginning of the buffer.
+ */
+ uint8_t *begin;
+ /**
+ * :member:`end` points to the one beyond of the last byte of the
+ * buffer
+ */
+ uint8_t *end;
+ /**
+ * :member:`pos` pointers to the start of data. Typically, this
+ * points to the point that next data should be read. Initially, it
+ * points to :member:`begin`.
+ */
+ uint8_t *pos;
+ /**
+ * :member:`last` points to the one beyond of the last data of the
+ * buffer. Typically, new data is written at this point.
+ * Initially, it points to :member:`begin`.
+ */
+ uint8_t *last;
+} nghttp3_buf;
+
+/**
+ * @function
+ *
+ * `nghttp3_buf_init` initializes empty |buf|.
+ */
+NGHTTP3_EXTERN void nghttp3_buf_init(nghttp3_buf *buf);
+
+/**
+ * @function
+ *
+ * `nghttp3_buf_free` frees resources allocated for |buf| using |mem|
+ * as memory allocator. :member:`buf->begin <nghttp3_buf.begin>` must
+ * be a heap buffer allocated by |mem|.
+ */
+NGHTTP3_EXTERN void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem);
+
+/**
+ * @function
+ *
+ * `nghttp3_buf_left` returns the number of additional bytes which can
+ * be written to the underlying buffer. In other words, it returns
+ * :member:`buf->end <nghttp3_buf.end>` - :member:`buf->last
+ * <nghttp3_buf.last>`.
+ */
+NGHTTP3_EXTERN size_t nghttp3_buf_left(const nghttp3_buf *buf);
+
+/**
+ * @function
+ *
+ * `nghttp3_buf_len` returns the number of bytes left to read. In
+ * other words, it returns :member:`buf->last <nghttp3_buf.last>` -
+ * :member:`buf->pos <nghttp3_buf.pos>`.
+ */
+NGHTTP3_EXTERN size_t nghttp3_buf_len(const nghttp3_buf *buf);
+
+/**
+ * @function
+ *
+ * `nghttp3_buf_reset` sets :member:`buf->pos <nghttp3_buf.pos>` and
+ * :member:`buf->last <nghttp3_buf.last>` to :member:`buf->begin
+ * <nghttp3_buf.begin>`.
+ */
+NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf);
+
+/**
+ * @macrosection
+ *
+ * Flags for header field name/value pair
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_NV_FLAG_NONE` indicates no flag set.
+ */
+#define NGHTTP3_NV_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_NV_FLAG_NEVER_INDEX` indicates that this name/value
+ * pair must not be indexed. Other implementation calls this bit as
+ * "sensitive".
+ */
+#define NGHTTP3_NV_FLAG_NEVER_INDEX 0x01u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_NV_FLAG_NO_COPY_NAME` is set solely by application.
+ * If this flag is set, the library does not make a copy of header
+ * field name. This could improve performance.
+ */
+#define NGHTTP3_NV_FLAG_NO_COPY_NAME 0x02u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_NV_FLAG_NO_COPY_VALUE` is set solely by
+ * application. If this flag is set, the library does not make a copy
+ * of header field value. This could improve performance.
+ */
+#define NGHTTP3_NV_FLAG_NO_COPY_VALUE 0x04u
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_nv` is the name/value pair, which mainly used to
+ * represent header fields.
+ */
+typedef struct nghttp3_nv {
+ /**
+ * :member:`name` is the header field name.
+ */
+ uint8_t *name;
+ /**
+ * :member:`value` is the header field value.
+ */
+ uint8_t *value;
+ /**
+ * :member:`namelen` is the length of the |name|, excluding
+ * terminating NULL.
+ */
+ size_t namelen;
+ /**
+ * :member:`valuelen` is the length of the |value|, excluding
+ * terminating NULL.
+ */
+ size_t valuelen;
+ /**
+ * :member:`flags` is bitwise OR of one or more of
+ * :macro:`NGHTTP3_NV_FLAG_* <NGHTTP3_NV_FLAG_NONE>`.
+ */
+ uint8_t flags;
+} nghttp3_nv;
+
+/* Generated by mkstatichdtbl.py */
+/**
+ * @enum
+ *
+ * :type:`nghttp3_qpack_token` defines HTTP header field name tokens
+ * to identify field name quickly. It appears in
+ * :member:`nghttp3_qpack_nv.token`.
+ */
+typedef enum nghttp3_qpack_token {
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__AUTHORITY` is a token for
+ * ``:authority``.
+ */
+ NGHTTP3_QPACK_TOKEN__AUTHORITY = 0,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__PATH` is a token for ``:path``.
+ */
+ NGHTTP3_QPACK_TOKEN__PATH = 8,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_AGE` is a token for ``age``.
+ */
+ NGHTTP3_QPACK_TOKEN_AGE = 43,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION` is a token for
+ * ``content-disposition``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION = 52,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH` is a token for
+ * ``content-length``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH = 55,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_COOKIE` is a token for ``cookie``.
+ */
+ NGHTTP3_QPACK_TOKEN_COOKIE = 68,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_DATE` is a token for ``date``.
+ */
+ NGHTTP3_QPACK_TOKEN_DATE = 69,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ETAG` is a token for ``etag``.
+ */
+ NGHTTP3_QPACK_TOKEN_ETAG = 71,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE` is a token for
+ * ``if-modified-since``.
+ */
+ NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE = 74,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH` is a token for
+ * ``if-none-match``.
+ */
+ NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH = 75,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_LAST_MODIFIED` is a token for
+ * ``last-modified``.
+ */
+ NGHTTP3_QPACK_TOKEN_LAST_MODIFIED = 77,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_LINK` is a token for ``link``.
+ */
+ NGHTTP3_QPACK_TOKEN_LINK = 78,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_LOCATION` is a token for ``location``.
+ */
+ NGHTTP3_QPACK_TOKEN_LOCATION = 79,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_REFERER` is a token for ``referer``.
+ */
+ NGHTTP3_QPACK_TOKEN_REFERER = 83,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_SET_COOKIE` is a token for
+ * ``set-cookie``.
+ */
+ NGHTTP3_QPACK_TOKEN_SET_COOKIE = 85,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__METHOD` is a token for ``:method``.
+ */
+ NGHTTP3_QPACK_TOKEN__METHOD = 1,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__SCHEME` is a token for ``:scheme``.
+ */
+ NGHTTP3_QPACK_TOKEN__SCHEME = 9,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__STATUS` is a token for ``:status``.
+ */
+ NGHTTP3_QPACK_TOKEN__STATUS = 11,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT` is a token for ``accept``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCEPT = 25,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING` is a token for
+ * ``accept-encoding``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING = 27,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES` is a token for
+ * ``accept-ranges``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES = 29,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS` is a
+ * token for ``access-control-allow-headers``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS = 32,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN` is a
+ * token for ``access-control-allow-origin``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 38,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CACHE_CONTROL` is a token for
+ * ``cache-control``.
+ */
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL = 46,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING` is a token for
+ * ``content-encoding``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING = 53,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_TYPE` is a token for
+ * ``content-type``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE = 57,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_RANGE` is a token for ``range``.
+ */
+ NGHTTP3_QPACK_TOKEN_RANGE = 82,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY` is a token
+ * for ``strict-transport-security``.
+ */
+ NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY = 86,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_VARY` is a token for ``vary``.
+ */
+ NGHTTP3_QPACK_TOKEN_VARY = 92,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS` is a token for
+ * ``x-content-type-options``.
+ */
+ NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS = 94,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION` is a token for
+ * ``x-xss-protection``.
+ */
+ NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION = 98,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE` is a token for
+ * ``accept-language``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE = 28,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS` is a
+ * token for ``access-control-allow-credentials``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS = 30,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS` is a
+ * token for ``access-control-allow-methods``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS = 35,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS` is a
+ * token for ``access-control-expose-headers``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS = 39,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS` is a
+ * token for ``access-control-request-headers``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS = 40,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD` is a
+ * token for ``access-control-request-method``.
+ */
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD = 41,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ALT_SVC` is a token for ``alt-svc``.
+ */
+ NGHTTP3_QPACK_TOKEN_ALT_SVC = 44,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_AUTHORIZATION` is a token for
+ * ``authorization``.
+ */
+ NGHTTP3_QPACK_TOKEN_AUTHORIZATION = 45,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY` is a token
+ * for ``content-security-policy``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY = 56,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_EARLY_DATA` is a token for
+ * ``early-data``.
+ */
+ NGHTTP3_QPACK_TOKEN_EARLY_DATA = 70,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_EXPECT_CT` is a token for
+ * ``expect-ct``.
+ */
+ NGHTTP3_QPACK_TOKEN_EXPECT_CT = 72,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_FORWARDED` is a token for
+ * ``forwarded``.
+ */
+ NGHTTP3_QPACK_TOKEN_FORWARDED = 73,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_IF_RANGE` is a token for ``if-range``.
+ */
+ NGHTTP3_QPACK_TOKEN_IF_RANGE = 76,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_ORIGIN` is a token for ``origin``.
+ */
+ NGHTTP3_QPACK_TOKEN_ORIGIN = 80,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_PURPOSE` is a token for ``purpose``.
+ */
+ NGHTTP3_QPACK_TOKEN_PURPOSE = 81,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_SERVER` is a token for ``server``.
+ */
+ NGHTTP3_QPACK_TOKEN_SERVER = 84,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN` is a token for
+ * ``timing-allow-origin``.
+ */
+ NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN = 89,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS` is a token
+ * for ``upgrade-insecure-requests``.
+ */
+ NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS = 90,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_USER_AGENT` is a token for
+ * ``user-agent``.
+ */
+ NGHTTP3_QPACK_TOKEN_USER_AGENT = 91,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR` is a token for
+ * ``x-forwarded-for``.
+ */
+ NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR = 95,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS` is a token for
+ * ``x-frame-options``.
+ */
+ NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS = 96,
+
+ /* Additional header fields for HTTP messaging validation */
+
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_HOST` is a token for ``host``.
+ */
+ NGHTTP3_QPACK_TOKEN_HOST = 1000,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_CONNECTION` is a token for
+ * ``connection``.
+ */
+ NGHTTP3_QPACK_TOKEN_CONNECTION,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_KEEP_ALIVE` is a token for
+ * ``keep-alive``.
+ */
+ NGHTTP3_QPACK_TOKEN_KEEP_ALIVE,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION` is a token for
+ * ``proxy-connection``.
+ */
+ NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING` is a token for
+ * ``transfer-encoding``.
+ */
+ NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_UPGRADE` is a token for ``upgrade``.
+ */
+ NGHTTP3_QPACK_TOKEN_UPGRADE,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_TE` is a token for ``te``.
+ */
+ NGHTTP3_QPACK_TOKEN_TE,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN__PROTOCOL` is a token for
+ * ``:protocol``.
+ */
+ NGHTTP3_QPACK_TOKEN__PROTOCOL,
+ /**
+ * :enum:`NGHTTP3_QPACK_TOKEN_PRIORITY` is a token for ``priority``.
+ */
+ NGHTTP3_QPACK_TOKEN_PRIORITY
+} nghttp3_qpack_token;
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_qpack_nv` represents header field name/value pair
+ * just like :type:`nghttp3_nv`. It is an extended version of
+ * :type:`nghttp3_nv` and has reference counted buffers and tokens
+ * which might be useful for applications.
+ */
+typedef struct nghttp3_qpack_nv {
+ /**
+ * :member:`name` is the buffer containing header field name.
+ * NULL-termination is guaranteed.
+ */
+ nghttp3_rcbuf *name;
+ /**
+ * :member:`value` is the buffer containing header field value.
+ * NULL-termination is guaranteed.
+ */
+ nghttp3_rcbuf *value;
+ /**
+ * :member:`token` is :type:`nghttp3_qpack_token` value of
+ * :member:`name`. It could be -1 if we have no token for that
+ * header field name.
+ */
+ int32_t token;
+ /**
+ * :member:`flags` is a bitwise OR of one or more of
+ * :macro:`NGHTTP3_NV_FLAG_* <NGHTTP3_NV_FLAG_NONE>`.
+ */
+ uint8_t flags;
+} nghttp3_qpack_nv;
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_qpack_encoder` is QPACK encoder.
+ */
+typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder;
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_new` initializes QPACK encoder. |pencoder|
+ * must be non-NULL pointer. |hard_max_dtable_capacity| is the upper
+ * bound of the dynamic table capacity. |mem| is a memory allocator.
+ * This function allocates memory for :type:`nghttp3_qpack_encoder`
+ * itself and assigns its pointer to |*pencoder| if it succeeds.
+ *
+ * The maximum dynamic table capacity is still 0. In order to change
+ * the maximum dynamic table capacity, call
+ * `nghttp3_qpack_encoder_set_max_dtable_capacity`.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ */
+NGHTTP3_EXTERN int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
+ size_t hard_max_dtable_capacity,
+ const nghttp3_mem *mem);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_del` frees memory allocated for |encoder|.
+ * This function frees memory pointed by |encoder| itself.
+ */
+NGHTTP3_EXTERN void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_encode` encodes the list of header fields
+ * |nva|. |nvlen| is the length of |nva|. |stream_id| is the
+ * identifier of the stream which this header fields belong to. This
+ * function writes header block prefix, encoded header fields, and
+ * encoder stream to |pbuf|, |rbuf|, and |ebuf| respectively. The
+ * :member:`nghttp3_buf.last` will be adjusted when data is written.
+ * An application should write |pbuf| and |rbuf| to the request stream
+ * in this order.
+ *
+ * The buffer pointed by |pbuf|, |rbuf|, and |ebuf| can be empty
+ * buffer. It is fine to pass a buffer initialized by
+ * `nghttp3_buf_init(buf) <nghttp3_buf_init>`. This function
+ * allocates memory for these buffers as necessary. In particular, it
+ * frees and expands buffer if the current capacity of buffer is not
+ * enough. If :member:`nghttp3_buf.begin` of any buffer is not NULL,
+ * it must be allocated by the same memory allocator passed to
+ * `nghttp3_qpack_encoder_new()`.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL`
+ * |encoder| is in unrecoverable error state and cannot be used
+ * anymore.
+ */
+NGHTTP3_EXTERN int nghttp3_qpack_encoder_encode(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, nghttp3_buf *rbuf,
+ nghttp3_buf *ebuf, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_read_decoder` reads decoder stream. The
+ * buffer pointed by |src| of length |srclen| contains decoder stream.
+ *
+ * This function returns the number of bytes read, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL`
+ * |encoder| is in unrecoverable error state and cannot be used
+ * anymore.
+ * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM`
+ * |encoder| is unable to process input because it is malformed.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_encoder_read_decoder(
+ nghttp3_qpack_encoder *encoder, const uint8_t *src, size_t srclen);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_set_max_dtable_capacity` sets max dynamic
+ * table capacity to |max_dtable_capacity|. If |max_dtable_capacity| is
+ * larger than ``hard_max_dtable_capacity`` parameter of
+ * `nghttp3_qpack_encoder_new`, it is truncated to the latter.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_encoder_set_max_dtable_capacity(nghttp3_qpack_encoder *encoder,
+ size_t max_dtable_capacity);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_set_max_blocked_streams` sets the number of
+ * streams which can be blocked to |max_blocked_streams|.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_encoder_set_max_blocked_streams(nghttp3_qpack_encoder *encoder,
+ size_t max_blocked_streams);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_ack_everything` tells |encoder| that all
+ * encoded header blocks are acknowledged. This function is provided
+ * for debugging purpose only. In HTTP/3, |encoder| knows this by
+ * reading decoder stream with `nghttp3_qpack_encoder_read_decoder()`.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_encoder_get_num_blocked_streams` returns the number
+ * of streams which are potentially blocked at decoder side.
+ */
+NGHTTP3_EXTERN size_t
+nghttp3_qpack_encoder_get_num_blocked_streams(nghttp3_qpack_encoder *encoder);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_qpack_stream_context` is a decoder context for an
+ * individual stream. Its state is per header block. In order to
+ * reuse this object for another header block, call
+ * `nghttp3_qpack_stream_context_reset`.
+ */
+typedef struct nghttp3_qpack_stream_context nghttp3_qpack_stream_context;
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_stream_context_new` initializes stream context.
+ * |psctx| must be non-NULL pointer. |stream_id| is stream ID. |mem|
+ * is a memory allocator. This function allocates memory for
+ * :type:`nghttp3_qpack_stream_context` itself and assigns its pointer
+ * to |*psctx| if it succeeds.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ */
+NGHTTP3_EXTERN int
+nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx,
+ int64_t stream_id, const nghttp3_mem *mem);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_stream_context_del` frees memory allocated for
+ * |sctx|. This function frees memory pointed by |sctx| itself.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_stream_context_get_ricnt` returns required insert
+ * count.
+ */
+NGHTTP3_EXTERN uint64_t
+nghttp3_qpack_stream_context_get_ricnt(nghttp3_qpack_stream_context *sctx);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_stream_context_reset` resets the state of |sctx|.
+ * Then it can be reused for an another header block in the same
+ * stream.
+ */
+NGHTTP3_EXTERN
+void nghttp3_qpack_stream_context_reset(nghttp3_qpack_stream_context *sctx);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_qpack_decoder` is QPACK decoder.
+ */
+typedef struct nghttp3_qpack_decoder nghttp3_qpack_decoder;
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_new` initializes QPACK decoder. |pdecoder|
+ * must be non-NULL pointer. |hard_max_dtable_capacity| is the upper
+ * bound of the dynamic table capacity. |max_blocked_streams| is the
+ * maximum number of streams which can be blocked. |mem| is a memory
+ * allocator. This function allocates memory for
+ * :type:`nghttp3_qpack_decoder` itself and assigns its pointer to
+ * |*pdecoder| if it succeeds.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ */
+NGHTTP3_EXTERN int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
+ const nghttp3_mem *mem);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_del` frees memory allocated for |decoder|.
+ * This function frees memory pointed by |decoder| itself.
+ */
+NGHTTP3_EXTERN void nghttp3_qpack_decoder_del(nghttp3_qpack_decoder *decoder);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_read_encoder` reads encoder stream. The
+ * buffer pointed by |src| of length |srclen| contains encoder stream.
+ *
+ * This function returns the number of bytes read, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL`
+ * |decoder| is in unrecoverable error state and cannot be used
+ * anymore.
+ * :macro:`NGHTTP3_ERR_QPACK_ENCODER_STREAM`
+ * Could not interpret encoder stream instruction.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_decoder_read_encoder(
+ nghttp3_qpack_decoder *decoder, const uint8_t *src, size_t srclen);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_get_icnt` returns insert count.
+ */
+NGHTTP3_EXTERN uint64_t
+nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder);
+
+/**
+ * @macrosection
+ *
+ * Flags for QPACK decoder
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_NONE` indicates that no flag set.
+ */
+#define NGHTTP3_QPACK_DECODE_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` indicates that a header
+ * field is successfully decoded.
+ */
+#define NGHTTP3_QPACK_DECODE_FLAG_EMIT 0x01u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` indicates that all header
+ * fields have been decoded.
+ */
+#define NGHTTP3_QPACK_DECODE_FLAG_FINAL 0x02u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` indicates that decoding
+ * has been blocked.
+ */
+#define NGHTTP3_QPACK_DECODE_FLAG_BLOCKED 0x04u
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_read_request` reads request stream. The
+ * request stream is given as the buffer pointed by |src| of length
+ * |srclen|. |sctx| is the stream context and it must be created by
+ * `nghttp3_qpack_stream_context_new()`. |*pflags| must be non-NULL
+ * pointer. |nv| must be non-NULL pointer.
+ *
+ * If this function succeeds, it assigns flags to |*pflags|. If
+ * |*pflags| has :macro:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` set, a
+ * decoded header field is assigned to |nv|. If |*pflags| has
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` set, all header fields
+ * have been successfully decoded. If |*pflags| has
+ * :macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` set, decoding is blocked
+ * due to required insert count.
+ *
+ * When a header field is decoded, an application receives it in |nv|.
+ * :member:`nv->name <nghttp3_qpack_nv.name>` and :member:`nv->value
+ * <nghttp3_qpack_nv.value>` are reference counted buffer, and their
+ * reference counts are already incremented for application use.
+ * Therefore, when application finishes processing the header field,
+ * it must call `nghttp3_rcbuf_decref(nv->name)
+ * <nghttp3_rcbuf_decref>` and `nghttp3_rcbuf_decref(nv->value)
+ * <nghttp3_rcbuf_decref>` or memory leak might occur. These
+ * :type:`nghttp3_rcbuf` objects hold the pointer to
+ * :type:`nghttp3_mem` that is passed to `nghttp3_qpack_decoder_new`
+ * (or either `nghttp3_conn_client_new` or `nghttp3_conn_server_new`
+ * if it is used indirectly). As long as these objects are alive, the
+ * pointed :type:`nghttp3_mem` object must be available. Otherwise,
+ * `nghttp3_rcbuf_decref` will cause undefined behavior.
+ *
+ * This function returns the number of bytes read, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL`
+ * |decoder| is in unrecoverable error state and cannot be used
+ * anymore.
+ * :macro:`NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED`
+ * Could not interpret header block instruction.
+ * :macro:`NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE`
+ * Header field is too large.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_decoder_read_request(
+ nghttp3_qpack_decoder *decoder, nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv, uint8_t *pflags, const uint8_t *src, size_t srclen,
+ int fin);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_write_decoder` writes decoder stream into
+ * |dbuf|.
+ *
+ * The caller must ensure that `nghttp3_buf_left(dbuf)
+ * <nghttp3_buf_left>` >=
+ * `nghttp3_qpack_decoder_get_decoder_streamlen(decoder)
+ * <nghttp3_qpack_decoder_get_decoder_streamlen>`.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder,
+ nghttp3_buf *dbuf);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_get_decoder_streamlen` returns the length of
+ * decoder stream.
+ */
+NGHTTP3_EXTERN size_t
+nghttp3_qpack_decoder_get_decoder_streamlen(nghttp3_qpack_decoder *decoder);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_cancel_stream` cancels header decoding for
+ * stream denoted by |stream_id|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ * :macro:`NGHTTP3_ERR_QPACK_FATAL`
+ * Decoder stream overflow.
+ */
+NGHTTP3_EXTERN int
+nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_set_max_dtable_capacity` sets
+ * |max_dtable_capacity| as maximum dynamic table size.
+ * |max_dtable_capacity| must be equal to or smaller than
+ * ``hard_max_dtable_capacity`` parameter of
+ * `nghttp3_qpack_decoder_new`. Normally, the maximum capacity is
+ * communicated in encoder stream. This function is provided for
+ * debugging and testing purpose.
+ *
+ * This function returns 0 if it succeeds, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT`
+ * |max_dtable_capacity| exceeds the upper bound of the dynamic
+ * table capacity.
+ */
+NGHTTP3_EXTERN int
+nghttp3_qpack_decoder_set_max_dtable_capacity(nghttp3_qpack_decoder *decoder,
+ size_t max_dtable_capacity);
+
+/**
+ * @function
+ *
+ * `nghttp3_qpack_decoder_set_max_concurrent_streams` tells |decoder|
+ * the maximum number of concurrent streams that a remote endpoint can
+ * open, including both bidirectional and unidirectional streams which
+ * potentially receive QPACK encoded HEADERS frame. This value is
+ * used as a hint to limit the length of decoder stream.
+ */
+NGHTTP3_EXTERN void
+nghttp3_qpack_decoder_set_max_concurrent_streams(nghttp3_qpack_decoder *decoder,
+ size_t max_concurrent_streams);
+
+/**
+ * @function
+ *
+ * `nghttp3_strerror` returns textual representation of |liberr|.
+ */
+NGHTTP3_EXTERN const char *nghttp3_strerror(int liberr);
+
+/**
+ * @function
+ *
+ * `nghttp3_err_infer_quic_app_error_code` returns a QUIC application
+ * error code which corresponds to |liberr|.
+ */
+NGHTTP3_EXTERN uint64_t nghttp3_err_infer_quic_app_error_code(int liberr);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_debug_vprintf_callback` is a callback function
+ * invoked when the library outputs debug logging. The function is
+ * called with arguments suitable for :manpage:`vfprintf(3)`.
+ *
+ * The debug output is only enabled if the library is built with
+ * :macro:`DEBUGBUILD` macro defined.
+ */
+typedef void (*nghttp3_debug_vprintf_callback)(const char *format,
+ va_list args);
+
+/**
+ * @function
+ *
+ * `nghttp3_set_debug_vprintf_callback` sets a debug output callback
+ * called by the library when built with :macro:`DEBUGBUILD` macro
+ * defined. If this option is not used, debug log is written into
+ * standard error output.
+ *
+ * For builds without :macro:`DEBUGBUILD` macro defined, this function
+ * is noop.
+ *
+ * Note that building with :macro:`DEBUGBUILD` may cause significant
+ * performance penalty to libnghttp3 because of extra processing. It
+ * should be used for debugging purpose only.
+ *
+ * .. Warning::
+ *
+ * Building with :macro:`DEBUGBUILD` may cause significant
+ * performance penalty to libnghttp3 because of extra processing.
+ * It should be used for debugging purpose only. We write this two
+ * times because this is important.
+ */
+NGHTTP3_EXTERN void nghttp3_set_debug_vprintf_callback(
+ nghttp3_debug_vprintf_callback debug_vprintf_callback);
+
+/**
+ * @macrosection
+ *
+ * Shutdown related constants
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID` specifies stream id sent
+ * by a server when it initiates graceful shutdown of the connection
+ * via `nghttp3_conn_submit_shutdown_notice`.
+ */
+#define NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID ((1ull << 62) - 4)
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID` specifies push id sent
+ * by a client when it initiates graceful shutdown of the connection
+ * via `nghttp3_conn_submit_shutdown_notice`.
+ */
+#define NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID ((1ull << 62) - 1)
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_conn` represents a single HTTP/3 connection.
+ */
+typedef struct nghttp3_conn nghttp3_conn;
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_acked_stream_data` is a callback function which is
+ * invoked when data sent on stream denoted by |stream_id| supplied
+ * from application is acknowledged by remote endpoint. The number of
+ * bytes acknowledged is given in |datalen|.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_acked_stream_data)(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t datalen, void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_conn_stream_close` is a callback function which is
+ * invoked when a stream identified by |stream_id| is closed.
+ * |app_error_code| indicates the reason of this closure.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_stream_close)(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_recv_data` is a callback function which is invoked
+ * when a part of request or response body on stream identified by
+ * |stream_id| is received. |data| points to the received data and
+ * its length is |datalen|.
+ *
+ * The application is responsible for increasing flow control credit
+ * by |datalen| bytes.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_recv_data)(nghttp3_conn *conn, int64_t stream_id,
+ const uint8_t *data, size_t datalen,
+ void *conn_user_data, void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_deferred_consume` is a callback function which is
+ * invoked when the library consumed |consumed| bytes for a stream
+ * identified by |stream_id|. This callback is used to notify the
+ * consumed bytes for stream blocked by QPACK decoder. The
+ * application is responsible for increasing flow control credit by
+ * |consumed| bytes.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_deferred_consume)(nghttp3_conn *conn, int64_t stream_id,
+ size_t consumed, void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_begin_headers` is a callback function which is
+ * invoked when an incoming header block section is started on a
+ * stream denoted by |stream_id|. Each header field is passed to
+ * application by :type:`nghttp3_recv_header` callback. And then
+ * :type:`nghttp3_end_headers` is called when a whole header block is
+ * processed.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_begin_headers)(nghttp3_conn *conn, int64_t stream_id,
+ void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_recv_header` is a callback function which is invoked
+ * when a header field is received on a stream denoted by |stream_id|.
+ * |name| contains a field name and |value| contains a field value.
+ * |token| is one of token defined in :type:`nghttp3_qpack_token` or
+ * -1 if no token is defined for |name|. |flags| is bitwise OR of
+ * zero or more of :macro:`NGHTTP3_NV_FLAG_* <NGHTTP3_NV_FLAG_NONE>`.
+ *
+ * The buffers for |name| and |value| are reference counted. If
+ * application needs to keep them, increment the reference count with
+ * `nghttp3_rcbuf_incref`. When they are no longer used, call
+ * `nghttp3_rcbuf_decref`.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_recv_header)(nghttp3_conn *conn, int64_t stream_id,
+ int32_t token, nghttp3_rcbuf *name,
+ nghttp3_rcbuf *value, uint8_t flags,
+ void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_end_headers` is a callback function which is invoked
+ * when an incoming header block has ended.
+ *
+ * If the stream ends with this header block, |fin| is set to nonzero.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_end_headers)(nghttp3_conn *conn, int64_t stream_id,
+ int fin, void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_end_stream` is a callback function which is invoked
+ * when the receiving side of stream is closed. For server, this
+ * callback function is invoked when HTTP request is received
+ * completely. For client, this callback function is invoked when
+ * HTTP response is received completely.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_end_stream)(nghttp3_conn *conn, int64_t stream_id,
+ void *conn_user_data, void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_stop_sending` is a callback function which is
+ * invoked when the library asks application to send STOP_SENDING to
+ * the stream identified by |stream_id|. |app_error_code| indicates
+ * the reason for this action.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_stop_sending)(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_reset_stream` is a callback function which is
+ * invoked when the library asks application to reset stream
+ * identified by |stream_id|. |app_error_code| indicates the reason
+ * for this action.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_reset_stream)(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *conn_user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_shutdown` is a callback function which is invoked
+ * when a shutdown is initiated by the remote endpoint. For client,
+ * |id| contains a stream id of a client initiated stream, for server,
+ * it contains a push id. All client streams with stream id or pushes
+ * with push id equal to or larger than |id| are guaranteed to not be
+ * processed by the remote endpoint.
+ *
+ * Parameter |id| for client can contain a special value
+ * :macro:`NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID` and for server it can
+ * contain special value
+ * :macro:`NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID`. These values signal
+ * request for graceful shutdown of the connection, triggered by
+ * remote endpoint's invocation of
+ * `nghttp3_conn_submit_shutdown_notice`.
+ *
+ * It is possible that this callback is invoked multiple times on a
+ * single connection, however the |id| can only stay the same or
+ * decrease, never increase.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
+ * caller immediately. Any values other than 0 is treated as
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ */
+typedef int (*nghttp3_shutdown)(nghttp3_conn *conn, int64_t id,
+ void *conn_user_data);
+
+#define NGHTTP3_CALLBACKS_VERSION_V1 1
+#define NGHTTP3_CALLBACKS_VERSION NGHTTP3_CALLBACKS_VERSION_V1
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_callbacks` holds a set of callback functions.
+ */
+typedef struct nghttp3_callbacks {
+ /**
+ * :member:`acked_stream_data` is a callback function which is
+ * invoked when data sent on a particular stream have been
+ * acknowledged by a remote endpoint.
+ */
+ nghttp3_acked_stream_data acked_stream_data;
+ /**
+ * :member:`stream_close` is a callback function which is invoked
+ * when a particular stream has closed.
+ */
+ nghttp3_stream_close stream_close;
+ /**
+ * :member:`recv_data` is a callback function which is invoked when
+ * stream data is received.
+ */
+ nghttp3_recv_data recv_data;
+ /**
+ * :member:`deferred_consume` is a callback function which is
+ * invoked when the library consumed data for a particular stream
+ * which had been blocked for synchronization between streams.
+ */
+ nghttp3_deferred_consume deferred_consume;
+ /**
+ * :member:`begin_headers` is a callback function which is invoked
+ * when a header block has started on a particular stream.
+ */
+ nghttp3_begin_headers begin_headers;
+ /**
+ * :member:`recv_header` is a callback function which is invoked
+ * when a single header field is received on a particular stream.
+ */
+ nghttp3_recv_header recv_header;
+ /**
+ * :member:`end_headers` is a callback function which is invoked
+ * when a header block has ended on a particular stream.
+ */
+ nghttp3_end_headers end_headers;
+ /**
+ * :member:`begin_trailers` is a callback function which is invoked
+ * when a trailer block has started on a particular stream.
+ */
+ nghttp3_begin_headers begin_trailers;
+ /**
+ * :member:`recv_trailer` is a callback function which is invoked
+ * when a single trailer field is received on a particular stream.
+ */
+ nghttp3_recv_header recv_trailer;
+ /**
+ * :member:`end_trailers` is a callback function which is invoked
+ * when a trailer block has ended on a particular stream.
+ */
+ nghttp3_end_headers end_trailers;
+ /**
+ * :member:`stop_sending` is a callback function which is invoked
+ * when the library asks application to send STOP_SENDING to a
+ * particular stream.
+ */
+ nghttp3_stop_sending stop_sending;
+ /**
+ * :member:`end_stream` is a callback function which is invoked when
+ * a receiving side of stream has been closed.
+ */
+ nghttp3_end_stream end_stream;
+ /**
+ * :member:`reset_stream` is a callback function which is invoked
+ * when the library asks application to reset stream (by sending
+ * RESET_STREAM).
+ */
+ nghttp3_reset_stream reset_stream;
+ /**
+ * :member:`shutdown` is a callback function which is invoked when
+ * the remote endpoint has signalled initiation of connection shutdown.
+ */
+ nghttp3_shutdown shutdown;
+} nghttp3_callbacks;
+
+#define NGHTTP3_SETTINGS_VERSION_V1 1
+#define NGHTTP3_SETTINGS_VERSION NGHTTP3_SETTINGS_VERSION_V1
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_settings` defines HTTP/3 settings.
+ */
+typedef struct nghttp3_settings {
+ /**
+ * :member:`max_field_section_size` specifies the maximum header
+ * section (block) size.
+ */
+ uint64_t max_field_section_size;
+ /**
+ * :member:`qpack_max_dtable_capacity` is the maximum size of QPACK
+ * dynamic table.
+ */
+ size_t qpack_max_dtable_capacity;
+ /**
+ * :member:`qpack_encoder_max_dtable_capacity` is the upper bound of
+ * QPACK dynamic table capacity that the QPACK encoder is willing to
+ * use. The effective maximum dynamic table capacity is the minimum
+ * of this field and the value of the received
+ * SETTINGS_QPACK_MAX_TABLE_CAPACITY. If this field is set to 0,
+ * the encoder does not use the dynamic table.
+ */
+ size_t qpack_encoder_max_dtable_capacity;
+ /**
+ * :member:`qpack_blocked_streams` is the maximum number of streams
+ * which can be blocked while they are being decoded.
+ */
+ size_t qpack_blocked_streams;
+ /**
+ * :member:`enable_connect_protocol`, if set to nonzero, enables
+ * Extended CONNECT Method (see
+ * https://datatracker.ietf.org/doc/html/rfc9220). Client ignores
+ * this field.
+ */
+ int enable_connect_protocol;
+} nghttp3_settings;
+
+/**
+ * @function
+ *
+ * `nghttp3_settings_default` fills |settings| with the default
+ * values.
+ *
+ * - :member:`max_field_section_size
+ * <nghttp3_settings.max_field_section_size>` = :expr:`((1ull << 62) - 1)`
+ * - :member:`qpack_max_dtable_capacity
+ * <nghttp3_settings.qpack_max_dtable_capacity>` = 0
+ * - :member:`qpack_encoder_max_dtable_capacity
+ * <nghttp3_settings.qpack_encoder_max_dtable_capacity>` = 4096
+ * - :member:`qpack_blocked_streams
+ * <nghttp3_settings.qpack_blocked_streams>` = 0
+ * - :member:`enable_connect_protocol
+ * <nghttp3_settings.enable_connect_protocol>` = 0
+ */
+NGHTTP3_EXTERN void
+nghttp3_settings_default_versioned(int settings_version,
+ nghttp3_settings *settings);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_client_new` creates :type:`nghttp3_conn` and
+ * initializes it for client use. The pointer to the object is stored
+ * in |*pconn|. If |mem| is ``NULL``, the memory allocator returned
+ * by `nghttp3_mem_default` is used.
+ */
+NGHTTP3_EXTERN int
+nghttp3_conn_client_new_versioned(nghttp3_conn **pconn, int callbacks_version,
+ const nghttp3_callbacks *callbacks,
+ int settings_version,
+ const nghttp3_settings *settings,
+ const nghttp3_mem *mem, void *conn_user_data);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_server_new` creates :type:`nghttp3_conn` and
+ * initializes it for server use. The pointer to the object is stored
+ * in |*pconn|. If |mem| is ``NULL``, the memory allocator returned
+ * by `nghttp3_mem_default` is used.
+ */
+NGHTTP3_EXTERN int
+nghttp3_conn_server_new_versioned(nghttp3_conn **pconn, int callbacks_version,
+ const nghttp3_callbacks *callbacks,
+ int settings_version,
+ const nghttp3_settings *settings,
+ const nghttp3_mem *mem, void *conn_user_data);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_del` frees resources allocated for |conn|.
+ */
+NGHTTP3_EXTERN void nghttp3_conn_del(nghttp3_conn *conn);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_bind_control_stream` binds stream denoted by
+ * |stream_id| to outgoing unidirectional control stream.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_STATE`
+ * Control stream has already corresponding stream ID.
+ *
+ * TBD
+ */
+NGHTTP3_EXTERN int nghttp3_conn_bind_control_stream(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_bind_qpack_streams` binds stream denoted by
+ * |qenc_stream_id| to outgoing QPACK encoder stream and stream
+ * denoted by |qdec_stream_id| to outgoing QPACK encoder stream.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_STATE`
+ * QPACK encoder/decoder stream have already corresponding stream
+ * IDs.
+ *
+ * TBD
+ */
+NGHTTP3_EXTERN int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn,
+ int64_t qenc_stream_id,
+ int64_t qdec_stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_read_stream` reads data |src| of length |srclen| on
+ * stream identified by |stream_id|. It returns the number of bytes
+ * consumed. The "consumed" means that application can increase flow
+ * control credit (both stream and connection) of underlying QUIC
+ * connection by that amount. It does not include the amount of data
+ * carried by DATA frame which contains application data (excluding
+ * any control or QPACK unidirectional streams) . See
+ * :type:`nghttp3_recv_data` to handle those bytes. If |fin| is
+ * nonzero, this is the last data from remote endpoint in this stream.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn,
+ int64_t stream_id,
+ const uint8_t *src,
+ size_t srclen, int fin);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_writev_stream` stores stream data to send to |vec| of
+ * length |veccnt| and returns the number of nghttp3_vec object in
+ * which it stored data. It stores stream ID to |*pstream_id|. An
+ * application has to call `nghttp3_conn_add_write_offset` to inform
+ * |conn| of the actual number of bytes that underlying QUIC stack
+ * accepted. |*pfin| will be nonzero if this is the last data to
+ * send. If there is no stream to write data or send fin, this
+ * function returns 0, and -1 is assigned to |*pstream_id|. This
+ * function may return 0 and |*pstream_id| is not -1 and |*pfin| is
+ * nonzero. It means 0 length data to |*pstream_id| and it is the
+ * last data to the stream. They must be passed to QUIC stack, and
+ * they are accepted, the application has to call
+ * `nghttp3_conn_add_write_offset`.
+ */
+NGHTTP3_EXTERN nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn,
+ int64_t *pstream_id,
+ int *pfin,
+ nghttp3_vec *vec,
+ size_t veccnt);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_add_write_offset` tells |conn| the number of bytes
+ * |n| for stream denoted by |stream_id| QUIC stack accepted.
+ *
+ * If stream has no data to send but just sends fin (closing the write
+ * side of a stream), the number of bytes sent is 0. It is important
+ * to call this function even if |n| is 0 in this case. It is safe to
+ * call this function if |n| is 0.
+ *
+ * `nghttp3_conn_writev_stream` must be called before calling this
+ * function to get data to send, and those data must be fed into QUIC
+ * stack.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_add_write_offset(nghttp3_conn *conn,
+ int64_t stream_id, size_t n);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_add_ack_offset` tells |conn| the number of bytes |n|
+ * for stream denoted by |stream_id| QUIC stack has acknowledged.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_add_ack_offset(nghttp3_conn *conn,
+ int64_t stream_id, uint64_t n);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_block_stream` tells the library that stream
+ * identified by |stream_id| is blocked due to QUIC flow control.
+ */
+NGHTTP3_EXTERN void nghttp3_conn_block_stream(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_unblock_stream` tells the library that stream
+ * identified by |stream_id| which was blocked by QUIC flow control is
+ * unblocked.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_unblock_stream(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_is_stream_writable` returns nonzero if a stream
+ * identified by |stream_id| is writable. It is not writable if:
+ *
+ * - the stream does not exist; or,
+ * - the stream is closed (e.g., `nghttp3_conn_close_stream` is
+ * called); or,
+ * - the stream is QUIC flow control blocked (e.g.,
+ * `nghttp3_conn_block_stream` is called); or,
+ * - the stream is input data blocked (e.g.,
+ * :macro:`NGHTTP3_ERR_WOULDBLOCK` is returned from
+ * :type:`nghttp3_read_data_callback`); or,
+ * - the stream is half-closed local (e.g.,
+ * `nghttp3_conn_shutdown_stream_write` is called).
+ */
+NGHTTP3_EXTERN int nghttp3_conn_is_stream_writable(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_shutdown_stream_write` tells the library that any
+ * further write operation to stream identified by |stream_id| is
+ * prohibited. This works like `nghttp3_conn_block_stream`, but it
+ * cannot be unblocked by `nghttp3_conn_unblock_stream`.
+ */
+NGHTTP3_EXTERN void nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_shutdown_stream_read` tells the library that
+ * read-side of stream denoted by |stream_id| is abruptly closed and
+ * any further incoming data and pending stream data should be
+ * discarded.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_shutdown_stream_read(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_resume_stream` resumes stream identified by
+ * |stream_id| which was previously unable to provide data.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_resume_stream(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_close_stream` closes stream identified by
+ * |stream_id|. |app_error_code| is the reason of the closure.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND`
+ * Stream not found.
+ * :macro:`NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM`
+ * A critical stream is closed.
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ */
+NGHTTP3_EXTERN int nghttp3_conn_close_stream(nghttp3_conn *conn,
+ int64_t stream_id,
+ uint64_t app_error_code);
+
+/**
+ * @macrosection
+ *
+ * Data flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_DATA_FLAG_NONE` indicates no flag set.
+ */
+#define NGHTTP3_DATA_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_DATA_FLAG_EOF` indicates that all request or
+ * response body has been provided to the library. It also indicates
+ * that sending side of stream is closed unless
+ * :macro:`NGHTTP3_DATA_FLAG_NO_END_STREAM` is given at the same time.
+ */
+#define NGHTTP3_DATA_FLAG_EOF 0x01u
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_DATA_FLAG_NO_END_STREAM` indicates that sending
+ * side of stream is not closed even if :macro:`NGHTTP3_DATA_FLAG_EOF`
+ * is set. Usually this flag is used to send trailer fields with
+ * `nghttp3_conn_submit_trailers()`. If
+ * `nghttp3_conn_submit_trailers()` has been called, regardless of
+ * this flag, the submitted trailer fields are sent.
+ */
+#define NGHTTP3_DATA_FLAG_NO_END_STREAM 0x02u
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_set_max_client_streams_bidi` tells |conn| the
+ * cumulative number of bidirectional streams that client can open.
+ */
+NGHTTP3_EXTERN void
+nghttp3_conn_set_max_client_streams_bidi(nghttp3_conn *conn,
+ uint64_t max_streams);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_set_max_concurrent_streams` tells |conn| the maximum
+ * number of concurrent streams that a remote endpoint can open,
+ * including both bidirectional and unidirectional streams which
+ * potentially receive QPACK encoded HEADERS frame. This value is
+ * used as a hint to limit the internal resource consumption.
+ */
+NGHTTP3_EXTERN void
+nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn,
+ size_t max_concurrent_streams);
+
+/**
+ * @functypedef
+ *
+ * :type:`nghttp3_read_data_callback` is a callback function invoked
+ * when the library asks an application to provide stream data for a
+ * stream denoted by |stream_id|.
+ *
+ * The library provides |vec| of length |veccnt| to the application.
+ * The application should fill data and its length to |vec|. It has
+ * to return the number of the filled objects. The application must
+ * retain data until they are safe to free. It is notified by
+ * :type:`nghttp3_acked_stream_data` callback.
+ *
+ * If this is the last data to send (or there is no data to send
+ * because all data have been sent already), set
+ * :macro:`NGHTTP3_DATA_FLAG_EOF` to |*pflags|.
+ *
+ * If the application is unable to provide data temporarily, return
+ * :macro:`NGHTTP3_ERR_WOULDBLOCK`. When it is ready to provide
+ * data, call `nghttp3_conn_resume_stream()`.
+ *
+ * The callback should return the number of objects in |vec| that the
+ * application filled if it succeeds, or
+ * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
+ *
+ * TODO Add NGHTTP3_ERR_TEMPORAL_CALLBACK_FAILURE to reset just this
+ * stream.
+ */
+typedef nghttp3_ssize (*nghttp3_read_data_callback)(
+ nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec, size_t veccnt,
+ uint32_t *pflags, void *conn_user_data, void *stream_user_data);
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_data_reader` specifies the way how to generate
+ * request or response body.
+ */
+typedef struct nghttp3_data_reader {
+ /**
+ * :member:`read_data` is a callback function to generate body.
+ */
+ nghttp3_read_data_callback read_data;
+} nghttp3_data_reader;
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_submit_request` submits HTTP request header fields
+ * and body on the stream identified by |stream_id|. |stream_id| must
+ * be a client initiated bidirectional stream. Only client can submit
+ * HTTP request. |nva| of length |nvlen| specifies HTTP request
+ * header fields. |dr| specifies a request body. If there is no
+ * request body, specify NULL. If |dr| is NULL, it implies the end of
+ * stream. |stream_user_data| is an opaque pointer attached to the
+ * stream.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_request(
+ nghttp3_conn *conn, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_data_reader *dr, void *stream_user_data);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_submit_info` submits HTTP non-final response header
+ * fields on the stream identified by |stream_id|. |nva| of length
+ * |nvlen| specifies HTTP response header fields.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_info(nghttp3_conn *conn,
+ int64_t stream_id,
+ const nghttp3_nv *nva,
+ size_t nvlen);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_submit_response` submits HTTP response header fields
+ * and body on the stream identified by |stream_id|. |nva| of length
+ * |nvlen| specifies HTTP response header fields. |dr| specifies a
+ * response body. If there is no response body, specify NULL. If
+ * |dr| is NULL, it implies the end of stream.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_response(nghttp3_conn *conn,
+ int64_t stream_id,
+ const nghttp3_nv *nva,
+ size_t nvlen,
+ const nghttp3_data_reader *dr);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_submit_trailers` submits HTTP trailer fields on the
+ * stream identified by |stream_id|. |nva| of length |nvlen|
+ * specifies HTTP trailer fields. Calling this function implies the
+ * end of stream.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_trailers(nghttp3_conn *conn,
+ int64_t stream_id,
+ const nghttp3_nv *nva,
+ size_t nvlen);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_submit_shutdown_notice` notifies the other endpoint
+ * to stop creating new stream. After a couple of RTTs later, call
+ * `nghttp3_conn_shutdown` to start graceful shutdown.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_shutdown` starts graceful shutdown. It should be
+ * called after `nghttp3_conn_submit_shutdown_notice` and a couple of
+ * RTT. After calling this function, the local endpoint starts
+ * rejecting new incoming streams. The existing streams are processed
+ * normally.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_shutdown(nghttp3_conn *conn);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_set_stream_user_data` sets |stream_user_data| to the
+ * stream identified by |stream_id|.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn,
+ int64_t stream_id,
+ void *stream_user_data);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_get_frame_payload_left` returns the number of bytes
+ * left to read current frame payload for a stream denoted by
+ * |stream_id|. If no such stream is found, it returns 0.
+ */
+NGHTTP3_EXTERN uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @macrosection
+ *
+ * HTTP stream priority flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_DEFAULT_URGENCY` is the default urgency level.
+ */
+#define NGHTTP3_DEFAULT_URGENCY 3
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_URGENCY_HIGH` is the highest urgency level.
+ */
+#define NGHTTP3_URGENCY_HIGH 0
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_URGENCY_LOW` is the lowest urgency level.
+ */
+#define NGHTTP3_URGENCY_LOW 7
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_URGENCY_LEVELS` is the number of urgency levels.
+ */
+#define NGHTTP3_URGENCY_LEVELS (NGHTTP3_URGENCY_LOW + 1)
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_pri` represents HTTP priority.
+ */
+typedef struct nghttp3_pri {
+ /**
+ * :member:`urgency` is the urgency of a stream, it must be in
+ * [:macro:`NGHTTP3_URGENCY_HIGH`, :macro:`NGHTTP3_URGENCY_LOW`],
+ * inclusive, and 0 is the highest urgency.
+ */
+ uint32_t urgency;
+ /**
+ * :member:`inc` indicates that a content can be processed
+ * incrementally or not. If inc is 0, it cannot be processed
+ * incrementally. If inc is 1, it can be processed incrementally.
+ * Other value is not permitted.
+ */
+ int inc;
+} nghttp3_pri;
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_get_stream_priority` stores stream priority of a
+ * stream denoted by |stream_id| into |*dest|. |stream_id| must
+ * identify client initiated bidirectional stream. Only server can
+ * use this function.
+ *
+ * This function must not be called if |conn| is initialized as
+ * client.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND`
+ * Stream not found.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_get_stream_priority(nghttp3_conn *conn,
+ nghttp3_pri *dest,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_set_stream_priority` updates priority of a stream
+ * denoted by |stream_id| with the value pointed by |pri|.
+ * |stream_id| must identify client initiated bidirectional stream.
+ *
+ * Both client and server can update stream priority with this
+ * function.
+ *
+ * If server updates stream priority with this function, it completely
+ * overrides stream priority set by client and the attempts to update
+ * priority by client are ignored.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT`
+ * |stream_id| is not a client initiated bidirectional stream ID.
+ * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND`
+ * Stream not found.
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ */
+NGHTTP3_EXTERN int nghttp3_conn_set_stream_priority(nghttp3_conn *conn,
+ int64_t stream_id,
+ const nghttp3_pri *pri);
+
+/**
+ * @function
+ *
+ * `nghttp3_conn_is_remote_qpack_encoder_stream` returns nonzero if a
+ * stream denoted by |stream_id| is QPACK encoder stream of a remote
+ * endpoint.
+ */
+NGHTTP3_EXTERN int
+nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `nghttp3_vec_len` returns the sum of length in |vec| of |cnt|
+ * elements.
+ */
+NGHTTP3_EXTERN uint64_t nghttp3_vec_len(const nghttp3_vec *vec, size_t cnt);
+
+/**
+ * @function
+ *
+ * `nghttp3_check_header_name` returns nonzero if HTTP header field
+ * name |name| of length |len| is valid according to
+ * :rfc:`7230#section-3.2`.
+ *
+ * Because this is a header field name in HTTP/3, the upper cased
+ * alphabet is treated as error.
+ */
+NGHTTP3_EXTERN int nghttp3_check_header_name(const uint8_t *name, size_t len);
+
+/**
+ * @function
+ *
+ * `nghttp3_check_header_value` returns nonzero if HTTP header field
+ * value |value| of length |len| is valid according to
+ * :rfc:`7230#section-3.2`.
+ */
+NGHTTP3_EXTERN int nghttp3_check_header_value(const uint8_t *value, size_t len);
+
+/**
+ * @function
+ *
+ * `nghttp3_http_parse_priority` parses priority HTTP header field
+ * stored in the buffer pointed by |value| of length |len|. If it
+ * successfully processed header field value, it stores the result
+ * into |*dest|. This function just overwrites what it sees in the
+ * header field value and does not initialize any field in |*dest|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT`
+ * The function could not parse the provided value.
+ */
+NGHTTP3_EXTERN int nghttp3_http_parse_priority(nghttp3_pri *dest,
+ const uint8_t *value,
+ size_t len);
+
+/**
+ * @macrosection
+ *
+ * nghttp3_info flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGHTTP3_VERSION_AGE` is the age of :type:`nghttp3_info`.
+ */
+#define NGHTTP3_VERSION_AGE 1
+
+/**
+ * @struct
+ *
+ * :type:`nghttp3_info` is what `nghttp3_version()` returns. It holds
+ * information about the particular nghttp3 version.
+ */
+typedef struct nghttp3_info {
+ /**
+ * :member:`age` is the age of this struct. This instance of
+ * nghttp3 sets it to :macro:`NGHTTP3_VERSION_AGE` but a future
+ * version may bump it and add more struct fields at the bottom
+ */
+ int age;
+ /**
+ * :member:`version_num` is the :macro:`NGHTTP3_VERSION_NUM` number
+ * (since age ==1)
+ */
+ int version_num;
+ /**
+ * :member:`version_str` points to the :macro:`NGHTTP3_VERSION`
+ * string (since age ==1)
+ */
+ const char *version_str;
+ /* -------- the above fields all exist when age == 1 */
+} nghttp3_info;
+
+/**
+ * @function
+ *
+ * `nghttp3_version` returns a pointer to a :type:`nghttp3_info`
+ * struct with version information about the run-time library in use.
+ * The |least_version| argument can be set to a 24 bit numerical value
+ * for the least accepted version number and if the condition is not
+ * met, this function will return a ``NULL``. Pass in 0 to skip the
+ * version checking.
+ */
+NGHTTP3_EXTERN const nghttp3_info *nghttp3_version(int least_version);
+
+/**
+ * @function
+ *
+ * `nghttp3_err_is_fatal` returns nonzero if |liberr| is a fatal
+ * error. |liberr| must be one of nghttp3 library error codes (which
+ * is defined as NGHTTP3_ERR_* macro, such as
+ * :macro:`NGHTTP3_ERR_NOMEM`).
+ */
+NGHTTP3_EXTERN int nghttp3_err_is_fatal(int liberr);
+
+/*
+ * Versioned function wrappers
+ */
+
+/*
+ * `nghttp3_settings_default` is a wrapper around
+ * `nghttp3_settings_default_versioned` to set the correct struct
+ * version.
+ */
+#define nghttp3_settings_default(SETTINGS) \
+ nghttp3_settings_default_versioned(NGHTTP3_SETTINGS_VERSION, (SETTINGS))
+
+/*
+ * `nghttp3_conn_client_new` is a wrapper around
+ * `nghttp3_conn_client_new_versioned` to set the correct struct
+ * version.
+ */
+#define nghttp3_conn_client_new(PCONN, CALLBACKS, SETTINGS, MEM, USER_DATA) \
+ nghttp3_conn_client_new_versioned((PCONN), NGHTTP3_CALLBACKS_VERSION, \
+ (CALLBACKS), NGHTTP3_SETTINGS_VERSION, \
+ (SETTINGS), (MEM), (USER_DATA))
+
+/*
+ * `nghttp3_conn_server_new` is a wrapper around
+ * `nghttp3_conn_server_new_versioned` to set the correct struct
+ * version.
+ */
+#define nghttp3_conn_server_new(PCONN, CALLBACKS, SETTINGS, MEM, USER_DATA) \
+ nghttp3_conn_server_new_versioned((PCONN), NGHTTP3_CALLBACKS_VERSION, \
+ (CALLBACKS), NGHTTP3_SETTINGS_VERSION, \
+ (SETTINGS), (MEM), (USER_DATA))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGHTTP3_H */
diff --git a/lib/includes/nghttp3/version.h.in b/lib/includes/nghttp3/version.h.in
new file mode 100644
index 0000000..1d86147
--- /dev/null
+++ b/lib/includes/nghttp3/version.h.in
@@ -0,0 +1,46 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_VERSION_H
+#define NGHTTP3_VERSION_H
+
+/**
+ * @macro
+ *
+ * Version number of the nghttp3 library release.
+ */
+#define NGHTTP3_VERSION "@PACKAGE_VERSION@"
+
+/**
+ * @macro
+ *
+ * Numerical representation of the version number of the nghttp3
+ * library release. This is a 24 bit number with 8 bits for major
+ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3
+ * becomes 0x010203.
+ */
+#define NGHTTP3_VERSION_NUM @PACKAGE_VERSION_NUM@
+
+#endif /* NGHTTP3_VERSION_H */
diff --git a/lib/libnghttp3.pc.in b/lib/libnghttp3.pc.in
new file mode 100644
index 0000000..c109b3e
--- /dev/null
+++ b/lib/libnghttp3.pc.in
@@ -0,0 +1,34 @@
+# nghttp3
+#
+# Copyright (c) 2019 nghttp3 contributors
+# Copyright (c) 2016 ngtcp2 contributors
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libnghttp3
+Description: nghttp3 library
+URL: https://github.com/ngtcp2/nghttp3
+Version: @VERSION@
+Libs: -L${libdir} -lnghttp3
+Cflags: -I${includedir}
diff --git a/lib/nghttp3_balloc.c b/lib/nghttp3_balloc.c
new file mode 100644
index 0000000..e134d0f
--- /dev/null
+++ b/lib/nghttp3_balloc.c
@@ -0,0 +1,91 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_balloc.h"
+
+#include <assert.h>
+
+#include "nghttp3_mem.h"
+
+void nghttp3_balloc_init(nghttp3_balloc *balloc, size_t blklen,
+ const nghttp3_mem *mem) {
+ assert((blklen & 0xfu) == 0);
+
+ balloc->mem = mem;
+ balloc->blklen = blklen;
+ balloc->head = NULL;
+ nghttp3_buf_wrap_init(&balloc->buf, (void *)"", 0);
+}
+
+void nghttp3_balloc_free(nghttp3_balloc *balloc) {
+ if (balloc == NULL) {
+ return;
+ }
+
+ nghttp3_balloc_clear(balloc);
+}
+
+void nghttp3_balloc_clear(nghttp3_balloc *balloc) {
+ nghttp3_memblock_hd *p, *next;
+
+ for (p = balloc->head; p; p = next) {
+ next = p->next;
+ nghttp3_mem_free(balloc->mem, p);
+ }
+
+ balloc->head = NULL;
+ nghttp3_buf_wrap_init(&balloc->buf, (void *)"", 0);
+}
+
+int nghttp3_balloc_get(nghttp3_balloc *balloc, void **pbuf, size_t n) {
+ uint8_t *p;
+ nghttp3_memblock_hd *hd;
+
+ assert(n <= balloc->blklen);
+
+ if (nghttp3_buf_left(&balloc->buf) < n) {
+ p = nghttp3_mem_malloc(balloc->mem, sizeof(nghttp3_memblock_hd) + 0x10u +
+ balloc->blklen);
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ hd = (nghttp3_memblock_hd *)(void *)p;
+ hd->next = balloc->head;
+ balloc->head = hd;
+ nghttp3_buf_wrap_init(
+ &balloc->buf,
+ (uint8_t *)(((uintptr_t)p + sizeof(nghttp3_memblock_hd) + 0xfu) &
+ ~(uintptr_t)0xfu),
+ balloc->blklen);
+ }
+
+ assert(((uintptr_t)balloc->buf.last & 0xfu) == 0);
+
+ *pbuf = balloc->buf.last;
+ balloc->buf.last += (n + 0xfu) & ~(uintptr_t)0xfu;
+
+ return 0;
+}
diff --git a/lib/nghttp3_balloc.h b/lib/nghttp3_balloc.h
new file mode 100644
index 0000000..e02f61d
--- /dev/null
+++ b/lib/nghttp3_balloc.h
@@ -0,0 +1,92 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_BALLOC_H
+#define NGHTTP3_BALLOC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_buf.h"
+
+typedef struct nghttp3_memblock_hd nghttp3_memblock_hd;
+
+/*
+ * nghttp3_memblock_hd is the header of memory block.
+ */
+struct nghttp3_memblock_hd {
+ nghttp3_memblock_hd *next;
+};
+
+/*
+ * nghttp3_balloc is a custom memory allocator. It allocates |blklen|
+ * bytes of memory at once on demand, and returns its slice when the
+ * allocation is requested.
+ */
+typedef struct nghttp3_balloc {
+ /* mem is the underlying memory allocator. */
+ const nghttp3_mem *mem;
+ /* blklen is the size of memory block. */
+ size_t blklen;
+ /* head points to the list of memory block allocated so far. */
+ nghttp3_memblock_hd *head;
+ /* buf wraps the current memory block for allocation requests. */
+ nghttp3_buf buf;
+} nghttp3_balloc;
+
+/*
+ * nghttp3_balloc_init initializes |balloc| with |blklen| which is the
+ * size of memory block.
+ */
+void nghttp3_balloc_init(nghttp3_balloc *balloc, size_t blklen,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_balloc_free releases all allocated memory blocks.
+ */
+void nghttp3_balloc_free(nghttp3_balloc *balloc);
+
+/*
+ * nghttp3_balloc_get allocates |n| bytes of memory and assigns its
+ * pointer to |*pbuf|.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_balloc_get(nghttp3_balloc *balloc, void **pbuf, size_t n);
+
+/*
+ * nghttp3_balloc_clear releases all allocated memory blocks and
+ * initializes its state.
+ */
+void nghttp3_balloc_clear(nghttp3_balloc *balloc);
+
+#endif /* NGHTTP3_BALLOC_H */
diff --git a/lib/nghttp3_buf.c b/lib/nghttp3_buf.c
new file mode 100644
index 0000000..aae075a
--- /dev/null
+++ b/lib/nghttp3_buf.c
@@ -0,0 +1,90 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_buf.h"
+
+void nghttp3_buf_init(nghttp3_buf *buf) {
+ buf->begin = buf->end = buf->pos = buf->last = NULL;
+}
+
+void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len) {
+ buf->begin = buf->pos = buf->last = src;
+ buf->end = buf->begin + len;
+}
+
+void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem) {
+ nghttp3_mem_free(mem, buf->begin);
+}
+
+size_t nghttp3_buf_left(const nghttp3_buf *buf) {
+ return (size_t)(buf->end - buf->last);
+}
+
+size_t nghttp3_buf_len(const nghttp3_buf *buf) {
+ return (size_t)(buf->last - buf->pos);
+}
+
+size_t nghttp3_buf_cap(const nghttp3_buf *buf) {
+ return (size_t)(buf->end - buf->begin);
+}
+
+void nghttp3_buf_reset(nghttp3_buf *buf) { buf->pos = buf->last = buf->begin; }
+
+int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem) {
+ uint8_t *p;
+ nghttp3_ssize pos_offset, last_offset;
+
+ if ((size_t)(buf->end - buf->begin) >= size) {
+ return 0;
+ }
+
+ pos_offset = buf->pos - buf->begin;
+ last_offset = buf->last - buf->begin;
+
+ p = nghttp3_mem_realloc(mem, buf->begin, size);
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ buf->begin = p;
+ buf->end = p + size;
+ buf->pos = p + pos_offset;
+ buf->last = p + last_offset;
+
+ return 0;
+}
+
+void nghttp3_buf_swap(nghttp3_buf *a, nghttp3_buf *b) {
+ nghttp3_buf c = *a;
+
+ *a = *b;
+ *b = c;
+}
+
+void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf,
+ nghttp3_buf_type type) {
+ tbuf->buf = *buf;
+ tbuf->type = type;
+}
diff --git a/lib/nghttp3_buf.h b/lib/nghttp3_buf.h
new file mode 100644
index 0000000..472a4b7
--- /dev/null
+++ b/lib/nghttp3_buf.h
@@ -0,0 +1,74 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_BUF_H
+#define NGHTTP3_BUF_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+
+void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len);
+
+/*
+ * nghttp3_buf_cap returns the capacity of the buffer. In other
+ * words, it returns buf->end - buf->begin.
+ */
+size_t nghttp3_buf_cap(const nghttp3_buf *buf);
+
+int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem);
+
+/*
+ * nghttp3_buf_swap swaps |a| and |b|.
+ */
+void nghttp3_buf_swap(nghttp3_buf *a, nghttp3_buf *b);
+
+typedef enum nghttp3_buf_type {
+ /* NGHTTP3_BUF_TYPE_PRIVATE indicates that memory is allocated for
+ this buffer only and should be freed after its use. */
+ NGHTTP3_BUF_TYPE_PRIVATE,
+ /* NGHTTP3_BUF_TYPE_SHARED indicates that buffer points to shared
+ memory. */
+ NGHTTP3_BUF_TYPE_SHARED,
+ /* NGHTTP3_BUF_TYPE_ALIEN indicates that the buffer points to a
+ memory which comes from outside of the library. */
+ NGHTTP3_BUF_TYPE_ALIEN,
+} nghttp3_buf_type;
+
+typedef struct nghttp3_typed_buf {
+ nghttp3_buf buf;
+ nghttp3_buf_type type;
+} nghttp3_typed_buf;
+
+void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf,
+ nghttp3_buf_type type);
+
+void nghttp3_typed_buf_free(nghttp3_typed_buf *tbuf);
+
+#endif /* NGHTTP3_BUF_H */
diff --git a/lib/nghttp3_conn.c b/lib/nghttp3_conn.c
new file mode 100644
index 0000000..c52be96
--- /dev/null
+++ b/lib/nghttp3_conn.c
@@ -0,0 +1,2532 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_conn.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "nghttp3_mem.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_err.h"
+#include "nghttp3_conv.h"
+#include "nghttp3_http.h"
+#include "nghttp3_unreachable.h"
+
+/* NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY is the upper bound of the
+ dynamic table capacity that QPACK encoder is willing to use. */
+#define NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY 4096
+
+/*
+ * conn_remote_stream_uni returns nonzero if |stream_id| is remote
+ * unidirectional stream ID.
+ */
+static int conn_remote_stream_uni(nghttp3_conn *conn, int64_t stream_id) {
+ if (conn->server) {
+ return (stream_id & 0x03) == 0x02;
+ }
+ return (stream_id & 0x03) == 0x03;
+}
+
+static int conn_call_begin_headers(nghttp3_conn *conn, nghttp3_stream *stream) {
+ int rv;
+
+ if (!conn->callbacks.begin_headers) {
+ return 0;
+ }
+
+ rv = conn->callbacks.begin_headers(conn, stream->node.id, conn->user_data,
+ stream->user_data);
+ if (rv != 0) {
+ /* TODO Allow ignore headers */
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_end_headers(nghttp3_conn *conn, nghttp3_stream *stream,
+ int fin) {
+ int rv;
+
+ if (!conn->callbacks.end_headers) {
+ return 0;
+ }
+
+ rv = conn->callbacks.end_headers(conn, stream->node.id, fin, conn->user_data,
+ stream->user_data);
+ if (rv != 0) {
+ /* TODO Allow ignore headers */
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_begin_trailers(nghttp3_conn *conn,
+ nghttp3_stream *stream) {
+ int rv;
+
+ if (!conn->callbacks.begin_trailers) {
+ return 0;
+ }
+
+ rv = conn->callbacks.begin_trailers(conn, stream->node.id, conn->user_data,
+ stream->user_data);
+ if (rv != 0) {
+ /* TODO Allow ignore headers */
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_end_trailers(nghttp3_conn *conn, nghttp3_stream *stream,
+ int fin) {
+ int rv;
+
+ if (!conn->callbacks.end_trailers) {
+ return 0;
+ }
+
+ rv = conn->callbacks.end_trailers(conn, stream->node.id, fin, conn->user_data,
+ stream->user_data);
+ if (rv != 0) {
+ /* TODO Allow ignore headers */
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_end_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
+ int rv;
+
+ if (!conn->callbacks.end_stream) {
+ return 0;
+ }
+
+ rv = conn->callbacks.end_stream(conn, stream->node.id, conn->user_data,
+ stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_stop_sending(nghttp3_conn *conn, nghttp3_stream *stream,
+ uint64_t app_error_code) {
+ int rv;
+
+ if (!conn->callbacks.stop_sending) {
+ return 0;
+ }
+
+ rv = conn->callbacks.stop_sending(conn, stream->node.id, app_error_code,
+ conn->user_data, stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_reset_stream(nghttp3_conn *conn, nghttp3_stream *stream,
+ uint64_t app_error_code) {
+ int rv;
+
+ if (!conn->callbacks.reset_stream) {
+ return 0;
+ }
+
+ rv = conn->callbacks.reset_stream(conn, stream->node.id, app_error_code,
+ conn->user_data, stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_deferred_consume(nghttp3_conn *conn,
+ nghttp3_stream *stream,
+ size_t nconsumed) {
+ int rv;
+
+ if (nconsumed == 0 || !conn->callbacks.deferred_consume) {
+ return 0;
+ }
+
+ rv = conn->callbacks.deferred_consume(conn, stream->node.id, nconsumed,
+ conn->user_data, stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int ricnt_less(const nghttp3_pq_entry *lhsx,
+ const nghttp3_pq_entry *rhsx) {
+ nghttp3_stream *lhs =
+ nghttp3_struct_of(lhsx, nghttp3_stream, qpack_blocked_pe);
+ nghttp3_stream *rhs =
+ nghttp3_struct_of(rhsx, nghttp3_stream, qpack_blocked_pe);
+
+ return lhs->qpack_sctx.ricnt < rhs->qpack_sctx.ricnt;
+}
+
+static int cycle_less(const nghttp3_pq_entry *lhsx,
+ const nghttp3_pq_entry *rhsx) {
+ const nghttp3_tnode *lhs = nghttp3_struct_of(lhsx, nghttp3_tnode, pe);
+ const nghttp3_tnode *rhs = nghttp3_struct_of(rhsx, nghttp3_tnode, pe);
+
+ if (lhs->cycle == rhs->cycle) {
+ return lhs->id < rhs->id;
+ }
+
+ return rhs->cycle - lhs->cycle <= NGHTTP3_TNODE_MAX_CYCLE_GAP;
+}
+
+static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version,
+ const nghttp3_callbacks *callbacks, int settings_version,
+ const nghttp3_settings *settings, const nghttp3_mem *mem,
+ void *user_data) {
+ int rv;
+ nghttp3_conn *conn;
+ size_t i;
+ (void)callbacks_version;
+ (void)settings_version;
+
+ if (mem == NULL) {
+ mem = nghttp3_mem_default();
+ }
+
+ conn = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_conn));
+ if (conn == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ nghttp3_objalloc_init(&conn->out_chunk_objalloc,
+ NGHTTP3_STREAM_MIN_CHUNK_SIZE * 16, mem);
+ nghttp3_objalloc_stream_init(&conn->stream_objalloc, 64, mem);
+
+ nghttp3_map_init(&conn->streams, mem);
+
+ rv = nghttp3_qpack_decoder_init(&conn->qdec,
+ settings->qpack_max_dtable_capacity,
+ settings->qpack_blocked_streams, mem);
+ if (rv != 0) {
+ goto qdec_init_fail;
+ }
+
+ rv = nghttp3_qpack_encoder_init(
+ &conn->qenc, settings->qpack_encoder_max_dtable_capacity, mem);
+ if (rv != 0) {
+ goto qenc_init_fail;
+ }
+
+ nghttp3_pq_init(&conn->qpack_blocked_streams, ricnt_less, mem);
+
+ for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) {
+ nghttp3_pq_init(&conn->sched[i].spq, cycle_less, mem);
+ }
+
+ nghttp3_idtr_init(&conn->remote.bidi.idtr, server, mem);
+
+ conn->callbacks = *callbacks;
+ conn->local.settings = *settings;
+ if (!server) {
+ conn->local.settings.enable_connect_protocol = 0;
+ }
+ nghttp3_settings_default(&conn->remote.settings);
+ conn->mem = mem;
+ conn->user_data = user_data;
+ conn->server = server;
+ conn->rx.goaway_id = NGHTTP3_VARINT_MAX + 1;
+ conn->tx.goaway_id = NGHTTP3_VARINT_MAX + 1;
+ conn->rx.max_stream_id_bidi = -4;
+
+ *pconn = conn;
+
+ return 0;
+
+qenc_init_fail:
+ nghttp3_qpack_decoder_free(&conn->qdec);
+qdec_init_fail:
+ nghttp3_map_free(&conn->streams);
+ nghttp3_objalloc_free(&conn->stream_objalloc);
+ nghttp3_objalloc_free(&conn->out_chunk_objalloc);
+ nghttp3_mem_free(mem, conn);
+
+ return rv;
+}
+
+int nghttp3_conn_client_new_versioned(nghttp3_conn **pconn,
+ int callbacks_version,
+ const nghttp3_callbacks *callbacks,
+ int settings_version,
+ const nghttp3_settings *settings,
+ const nghttp3_mem *mem, void *user_data) {
+ int rv;
+
+ rv = conn_new(pconn, /* server = */ 0, callbacks_version, callbacks,
+ settings_version, settings, mem, user_data);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return 0;
+}
+
+int nghttp3_conn_server_new_versioned(nghttp3_conn **pconn,
+ int callbacks_version,
+ const nghttp3_callbacks *callbacks,
+ int settings_version,
+ const nghttp3_settings *settings,
+ const nghttp3_mem *mem, void *user_data) {
+ int rv;
+
+ rv = conn_new(pconn, /* server = */ 1, callbacks_version, callbacks,
+ settings_version, settings, mem, user_data);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return 0;
+}
+
+static int free_stream(void *data, void *ptr) {
+ nghttp3_stream *stream = data;
+
+ (void)ptr;
+
+ nghttp3_stream_del(stream);
+
+ return 0;
+}
+
+void nghttp3_conn_del(nghttp3_conn *conn) {
+ size_t i;
+
+ if (conn == NULL) {
+ return;
+ }
+
+ nghttp3_buf_free(&conn->tx.qpack.ebuf, conn->mem);
+ nghttp3_buf_free(&conn->tx.qpack.rbuf, conn->mem);
+
+ nghttp3_idtr_free(&conn->remote.bidi.idtr);
+
+ for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) {
+ nghttp3_pq_free(&conn->sched[i].spq);
+ }
+
+ nghttp3_pq_free(&conn->qpack_blocked_streams);
+
+ nghttp3_qpack_encoder_free(&conn->qenc);
+ nghttp3_qpack_decoder_free(&conn->qdec);
+
+ nghttp3_map_each_free(&conn->streams, free_stream, NULL);
+ nghttp3_map_free(&conn->streams);
+
+ nghttp3_objalloc_free(&conn->stream_objalloc);
+ nghttp3_objalloc_free(&conn->out_chunk_objalloc);
+
+ nghttp3_mem_free(conn->mem, conn);
+}
+
+static int conn_bidi_idtr_open(nghttp3_conn *conn, int64_t stream_id) {
+ int rv;
+
+ rv = nghttp3_idtr_open(&conn->remote.bidi.idtr, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (nghttp3_ksl_len(&conn->remote.bidi.idtr.gap.gap) > 32) {
+ nghttp3_gaptr_drop_first_gap(&conn->remote.bidi.idtr.gap);
+ }
+
+ return 0;
+}
+
+nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id,
+ const uint8_t *src, size_t srclen,
+ int fin) {
+ nghttp3_stream *stream;
+ size_t bidi_nproc;
+ int rv;
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ /* TODO Assert idtr */
+ /* QUIC transport ensures that this is new stream. */
+ if (conn->server) {
+ if (nghttp3_client_stream_bidi(stream_id)) {
+ rv = conn_bidi_idtr_open(conn, stream_id);
+ if (rv != 0) {
+ if (nghttp3_err_is_fatal(rv)) {
+ return rv;
+ }
+
+ /* Ignore return value. We might drop the first gap if there
+ are many gaps if QUIC stack allows too many holes in stream
+ ID space. idtr is used to decide whether PRIORITY_UPDATE
+ frame should be ignored or not and the frame is optional.
+ Ignoring them causes no harm. */
+ }
+
+ conn->rx.max_stream_id_bidi =
+ nghttp3_max(conn->rx.max_stream_id_bidi, stream_id);
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) &&
+ conn->tx.goaway_id <= stream_id) {
+ stream->rstate.state = NGHTTP3_REQ_STREAM_STATE_IGN_REST;
+
+ rv = nghttp3_conn_reject_stream(conn, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ } else {
+ /* unidirectional stream */
+ if (srclen == 0 && fin) {
+ return 0;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
+ stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
+ } else if (nghttp3_stream_uni(stream_id)) {
+ if (srclen == 0 && fin) {
+ return 0;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ stream->tx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ } else {
+ /* client doesn't expect to receive new bidirectional stream
+ from server. */
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
+ }
+ } else if (conn->server) {
+ if (nghttp3_client_stream_bidi(stream_id)) {
+ if (stream->rx.hstate == NGHTTP3_HTTP_STATE_NONE) {
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
+ stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
+ }
+ }
+ }
+
+ if (srclen == 0 && !fin) {
+ return 0;
+ }
+
+ if (nghttp3_stream_uni(stream_id)) {
+ return nghttp3_conn_read_uni(conn, stream, src, srclen, fin);
+ }
+
+ if (fin) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_READ_EOF;
+ }
+ return nghttp3_conn_read_bidi(conn, &bidi_nproc, stream, src, srclen, fin);
+}
+
+static nghttp3_ssize conn_read_type(nghttp3_conn *conn, nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen,
+ int fin) {
+ nghttp3_stream_read_state *rstate = &stream->rstate;
+ nghttp3_varint_read_state *rvint = &rstate->rvint;
+ nghttp3_ssize nread;
+ int64_t stream_type;
+
+ assert(srclen);
+
+ nread = nghttp3_read_varint(rvint, src, srclen, fin);
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+
+ if (rvint->left) {
+ return nread;
+ }
+
+ stream_type = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ switch (stream_type) {
+ case NGHTTP3_STREAM_TYPE_CONTROL:
+ if (conn->flags & NGHTTP3_CONN_FLAG_CONTROL_OPENED) {
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
+ }
+ conn->flags |= NGHTTP3_CONN_FLAG_CONTROL_OPENED;
+ stream->type = NGHTTP3_STREAM_TYPE_CONTROL;
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE;
+ break;
+ case NGHTTP3_STREAM_TYPE_PUSH:
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
+ case NGHTTP3_STREAM_TYPE_QPACK_ENCODER:
+ if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED) {
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
+ }
+ conn->flags |= NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED;
+ stream->type = NGHTTP3_STREAM_TYPE_QPACK_ENCODER;
+ break;
+ case NGHTTP3_STREAM_TYPE_QPACK_DECODER:
+ if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED) {
+ return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
+ }
+ conn->flags |= NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED;
+ stream->type = NGHTTP3_STREAM_TYPE_QPACK_DECODER;
+ break;
+ default:
+ stream->type = NGHTTP3_STREAM_TYPE_UNKNOWN;
+ break;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED;
+
+ return nread;
+}
+
+static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream);
+
+nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen,
+ int fin) {
+ nghttp3_ssize nread = 0;
+ nghttp3_ssize nconsumed = 0;
+ int rv;
+
+ assert(srclen || fin);
+
+ if (!(stream->flags & NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED)) {
+ if (srclen == 0 && fin) {
+ /* Ignore stream if it is closed before reading stream header.
+ If it is closed while reading it, return error, making it
+ consistent in our code base. */
+ if (stream->rstate.rvint.left) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+
+ rv = conn_delete_stream(conn, stream);
+ assert(0 == rv);
+
+ return 0;
+ }
+ nread = conn_read_type(conn, stream, src, srclen, fin);
+ if (nread < 0) {
+ return (int)nread;
+ }
+ if (!(stream->flags & NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED)) {
+ assert((size_t)nread == srclen);
+ return (nghttp3_ssize)srclen;
+ }
+
+ src += nread;
+ srclen -= (size_t)nread;
+
+ if (srclen == 0) {
+ return nread;
+ }
+ }
+
+ switch (stream->type) {
+ case NGHTTP3_STREAM_TYPE_CONTROL:
+ if (fin) {
+ return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM;
+ }
+ nconsumed = nghttp3_conn_read_control(conn, stream, src, srclen);
+ break;
+ case NGHTTP3_STREAM_TYPE_QPACK_ENCODER:
+ if (fin) {
+ return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM;
+ }
+ nconsumed = nghttp3_conn_read_qpack_encoder(conn, src, srclen);
+ break;
+ case NGHTTP3_STREAM_TYPE_QPACK_DECODER:
+ if (fin) {
+ return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM;
+ }
+ nconsumed = nghttp3_conn_read_qpack_decoder(conn, src, srclen);
+ break;
+ case NGHTTP3_STREAM_TYPE_UNKNOWN:
+ nconsumed = (nghttp3_ssize)srclen;
+
+ rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_STREAM_CREATION_ERROR);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ if (nconsumed < 0) {
+ return nconsumed;
+ }
+
+ return nread + nconsumed;
+}
+
+static int frame_fin(nghttp3_stream_read_state *rstate, size_t len) {
+ return (int64_t)len >= rstate->left;
+}
+
+nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
+ nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen) {
+ const uint8_t *p = src, *end = src + srclen;
+ int rv;
+ nghttp3_stream_read_state *rstate = &stream->rstate;
+ nghttp3_varint_read_state *rvint = &rstate->rvint;
+ nghttp3_ssize nread;
+ size_t nconsumed = 0;
+ int busy = 0;
+ size_t len;
+ const uint8_t *pri_field_value = NULL;
+ size_t pri_field_valuelen = 0;
+
+ assert(srclen);
+
+ for (; p != end || busy;) {
+ busy = 0;
+ switch (rstate->state) {
+ case NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE:
+ assert(end - p > 0);
+ nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), /* fin = */ 0);
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ rstate->fr.hd.type = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH;
+ if (p == end) {
+ break;
+ }
+ /* Fall through */
+ case NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH:
+ assert(end - p > 0);
+ nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), /* fin = */ 0);
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ rstate->left = rstate->fr.hd.length = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ if (!(conn->flags & NGHTTP3_CONN_FLAG_SETTINGS_RECVED)) {
+ if (rstate->fr.hd.type != NGHTTP3_FRAME_SETTINGS) {
+ return NGHTTP3_ERR_H3_MISSING_SETTINGS;
+ }
+ conn->flags |= NGHTTP3_CONN_FLAG_SETTINGS_RECVED;
+ } else if (rstate->fr.hd.type == NGHTTP3_FRAME_SETTINGS) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+
+ switch (rstate->fr.hd.type) {
+ case NGHTTP3_FRAME_SETTINGS:
+ /* SETTINGS frame might be empty. */
+ if (rstate->left == 0) {
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ }
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS;
+ break;
+ case NGHTTP3_FRAME_GOAWAY:
+ if (rstate->left == 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_GOAWAY;
+ break;
+ case NGHTTP3_FRAME_MAX_PUSH_ID:
+ if (!conn->server) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ if (rstate->left == 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID;
+ break;
+ case NGHTTP3_FRAME_PRIORITY_UPDATE:
+ if (!conn->server) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ if (rstate->left == 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID;
+ break;
+ case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID:
+ /* We do not support push */
+ return NGHTTP3_ERR_H3_ID_ERROR;
+ case NGHTTP3_FRAME_CANCEL_PUSH: /* We do not support push */
+ case NGHTTP3_FRAME_DATA:
+ case NGHTTP3_FRAME_HEADERS:
+ case NGHTTP3_FRAME_PUSH_PROMISE:
+ case NGHTTP3_H2_FRAME_PRIORITY:
+ case NGHTTP3_H2_FRAME_PING:
+ case NGHTTP3_H2_FRAME_WINDOW_UPDATE:
+ case NGHTTP3_H2_FRAME_CONTINUATION:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ default:
+ /* TODO Handle reserved frame type */
+ busy = 1;
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME;
+ break;
+ }
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_SETTINGS:
+ for (; p != end;) {
+ if (rstate->left == 0) {
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ }
+ /* Read Identifier */
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+ if (rvint->left) {
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID;
+ return (nghttp3_ssize)nconsumed;
+ }
+ rstate->fr.settings.iv[0].id = (uint64_t)rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ /* Read Value */
+ if (rstate->left == 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ len -= (size_t)nread;
+ if (len == 0) {
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE;
+ break;
+ }
+
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+ if (rvint->left) {
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE;
+ return (nghttp3_ssize)nconsumed;
+ }
+ rstate->fr.settings.iv[0].value = (uint64_t)rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ rv =
+ nghttp3_conn_on_settings_entry_received(conn, &rstate->fr.settings);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+ rstate->fr.settings.iv[0].id = (uint64_t)rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ if (rstate->left == 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE;
+
+ if (p == end) {
+ return (nghttp3_ssize)nconsumed;
+ }
+ /* Fall through */
+ case NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+ rstate->fr.settings.iv[0].value = (uint64_t)rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ rv = nghttp3_conn_on_settings_entry_received(conn, &rstate->fr.settings);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (rstate->left) {
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS;
+ break;
+ }
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_GOAWAY:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ if (!conn->server && !nghttp3_client_stream_bidi(rvint->acc)) {
+ return NGHTTP3_ERR_H3_ID_ERROR;
+ }
+ if (conn->rx.goaway_id < rvint->acc) {
+ return NGHTTP3_ERR_H3_ID_ERROR;
+ }
+
+ conn->flags |= NGHTTP3_CONN_FLAG_GOAWAY_RECVED;
+ conn->rx.goaway_id = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ if (conn->callbacks.shutdown) {
+ rv =
+ conn->callbacks.shutdown(conn, conn->rx.goaway_id, conn->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ }
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID:
+ /* server side only */
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ if (conn->local.uni.max_pushes > (uint64_t)rvint->acc + 1) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ conn->local.uni.max_pushes = (uint64_t)rvint->acc + 1;
+ nghttp3_varint_read_state_reset(rvint);
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID:
+ /* server side only */
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len));
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+ if (rvint->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ rstate->fr.priority_update.pri_elem_id = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ if (rstate->left == 0) {
+ rstate->fr.priority_update.pri.urgency = NGHTTP3_DEFAULT_URGENCY;
+ rstate->fr.priority_update.pri.inc = 0;
+
+ rv = nghttp3_conn_on_priority_update(conn, &rstate->fr.priority_update);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ }
+
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE;
+
+ /* Fall through */
+ case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE:
+ /* We need to buffer Priority Field Value because it might be
+ fragmented. */
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ assert(len > 0);
+ if (conn->rx.pri_fieldbuflen == 0 && rstate->left == (int64_t)len) {
+ /* Everything is in the input buffer. Apply same length
+ limit we impose when buffering the field. */
+ if (len > sizeof(conn->rx.pri_fieldbuf)) {
+ busy = 1;
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME;
+ break;
+ }
+
+ pri_field_value = p;
+ pri_field_valuelen = len;
+ } else if (len + conn->rx.pri_fieldbuflen >
+ sizeof(conn->rx.pri_fieldbuf)) {
+ busy = 1;
+ rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME;
+ break;
+ } else {
+ memcpy(conn->rx.pri_fieldbuf + conn->rx.pri_fieldbuflen, p, len);
+ conn->rx.pri_fieldbuflen += len;
+
+ if (rstate->left == (int64_t)len) {
+ pri_field_value = conn->rx.pri_fieldbuf;
+ pri_field_valuelen = conn->rx.pri_fieldbuflen;
+ }
+ }
+
+ p += len;
+ nconsumed += len;
+ rstate->left -= (int64_t)len;
+
+ if (rstate->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ rstate->fr.priority_update.pri.urgency = NGHTTP3_DEFAULT_URGENCY;
+ rstate->fr.priority_update.pri.inc = 0;
+
+ if (nghttp3_http_parse_priority(&rstate->fr.priority_update.pri,
+ pri_field_value,
+ pri_field_valuelen) != 0) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+
+ rv = nghttp3_conn_on_priority_update(conn, &rstate->fr.priority_update);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn->rx.pri_fieldbuflen = 0;
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ p += len;
+ nconsumed += len;
+ rstate->left -= (int64_t)len;
+
+ if (rstate->left) {
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+ }
+
+ return (nghttp3_ssize)nconsumed;
+}
+
+static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
+ int bidi = nghttp3_client_stream_bidi(stream->node.id);
+ int rv;
+
+ rv = conn_call_deferred_consume(conn, stream,
+ nghttp3_stream_get_buffered_datalen(stream));
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (bidi && conn->callbacks.stream_close) {
+ rv = conn->callbacks.stream_close(conn, stream->node.id, stream->error_code,
+ conn->user_data, stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ }
+
+ rv =
+ nghttp3_map_remove(&conn->streams, (nghttp3_map_key_type)stream->node.id);
+
+ assert(0 == rv);
+
+ nghttp3_stream_del(stream);
+
+ return 0;
+}
+
+static int conn_process_blocked_stream_data(nghttp3_conn *conn,
+ nghttp3_stream *stream) {
+ nghttp3_buf *buf;
+ size_t nproc;
+ nghttp3_ssize nconsumed;
+ int rv;
+ size_t len;
+
+ assert(nghttp3_client_stream_bidi(stream->node.id));
+
+ for (;;) {
+ len = nghttp3_ringbuf_len(&stream->inq);
+ if (len == 0) {
+ break;
+ }
+
+ buf = nghttp3_ringbuf_get(&stream->inq, 0);
+
+ nconsumed = nghttp3_conn_read_bidi(
+ conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf),
+ len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF));
+ if (nconsumed < 0) {
+ return (int)nconsumed;
+ }
+
+ buf->pos += nproc;
+
+ rv = conn_call_deferred_consume(conn, stream, (size_t)nconsumed);
+ if (rv != 0) {
+ return 0;
+ }
+
+ if (nghttp3_buf_len(buf) == 0) {
+ nghttp3_buf_free(buf, stream->mem);
+ nghttp3_ringbuf_pop_front(&stream->inq);
+ }
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) {
+ break;
+ }
+ }
+
+ if (!(stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) &&
+ (stream->flags & NGHTTP3_STREAM_FLAG_CLOSED)) {
+ assert(stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX);
+
+ rv = conn_delete_stream(conn, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn,
+ const uint8_t *src,
+ size_t srclen) {
+ nghttp3_ssize nconsumed =
+ nghttp3_qpack_decoder_read_encoder(&conn->qdec, src, srclen);
+ nghttp3_stream *stream;
+ int rv;
+
+ if (nconsumed < 0) {
+ return nconsumed;
+ }
+
+ for (; !nghttp3_pq_empty(&conn->qpack_blocked_streams);) {
+ stream = nghttp3_struct_of(nghttp3_pq_top(&conn->qpack_blocked_streams),
+ nghttp3_stream, qpack_blocked_pe);
+ if (nghttp3_qpack_stream_context_get_ricnt(&stream->qpack_sctx) >
+ nghttp3_qpack_decoder_get_icnt(&conn->qdec)) {
+ break;
+ }
+
+ nghttp3_conn_qpack_blocked_streams_pop(conn);
+ stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX;
+ stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED;
+
+ rv = conn_process_blocked_stream_data(conn, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return nconsumed;
+}
+
+nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn,
+ const uint8_t *src,
+ size_t srclen) {
+ return nghttp3_qpack_encoder_read_decoder(&conn->qenc, src, srclen);
+}
+
+static nghttp3_tnode *stream_get_sched_node(nghttp3_stream *stream) {
+ return &stream->node;
+}
+
+static int conn_update_stream_priority(nghttp3_conn *conn,
+ nghttp3_stream *stream, uint8_t pri) {
+ assert(nghttp3_client_stream_bidi(stream->node.id));
+
+ if (stream->node.pri == pri) {
+ return 0;
+ }
+
+ nghttp3_conn_unschedule_stream(conn, stream);
+
+ stream->node.pri = pri;
+
+ if (nghttp3_stream_require_schedule(stream)) {
+ return nghttp3_conn_schedule_stream(conn, stream);
+ }
+
+ return 0;
+}
+
+nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
+ nghttp3_stream *stream, const uint8_t *src,
+ size_t srclen, int fin) {
+ const uint8_t *p = src, *end = src ? src + srclen : src;
+ int rv;
+ nghttp3_stream_read_state *rstate = &stream->rstate;
+ nghttp3_varint_read_state *rvint = &rstate->rvint;
+ nghttp3_ssize nread;
+ size_t nconsumed = 0;
+ int busy = 0;
+ size_t len;
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_RD) {
+ *pnproc = srclen;
+
+ return (nghttp3_ssize)srclen;
+ }
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) {
+ *pnproc = 0;
+
+ if (srclen == 0) {
+ return 0;
+ }
+
+ rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p));
+ if (rv != 0) {
+ return rv;
+ }
+ return 0;
+ }
+
+ for (; p != end || busy;) {
+ busy = 0;
+ switch (rstate->state) {
+ case NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE:
+ assert(end - p > 0);
+ nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin);
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ if (rvint->left) {
+ goto almost_done;
+ }
+
+ rstate->fr.hd.type = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+ rstate->state = NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH;
+ if (p == end) {
+ goto almost_done;
+ }
+ /* Fall through */
+ case NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH:
+ assert(end - p > 0);
+ nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin);
+ if (nread < 0) {
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ if (rvint->left) {
+ goto almost_done;
+ }
+
+ rstate->left = rstate->fr.hd.length = rvint->acc;
+ nghttp3_varint_read_state_reset(rvint);
+
+ switch (rstate->fr.hd.type) {
+ case NGHTTP3_FRAME_DATA:
+ rv = nghttp3_stream_transit_rx_http_state(
+ stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN);
+ if (rv != 0) {
+ return rv;
+ }
+ /* DATA frame might be empty. */
+ if (rstate->left == 0) {
+ rv = nghttp3_stream_transit_rx_http_state(
+ stream, NGHTTP3_HTTP_EVENT_DATA_END);
+ assert(0 == rv);
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ }
+ rstate->state = NGHTTP3_REQ_STREAM_STATE_DATA;
+ break;
+ case NGHTTP3_FRAME_HEADERS:
+ rv = nghttp3_stream_transit_rx_http_state(
+ stream, NGHTTP3_HTTP_EVENT_HEADERS_BEGIN);
+ if (rv != 0) {
+ return rv;
+ }
+ if (rstate->left == 0) {
+ rv = nghttp3_stream_empty_headers_allowed(stream);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = nghttp3_stream_transit_rx_http_state(
+ stream, NGHTTP3_HTTP_EVENT_HEADERS_END);
+ assert(0 == rv);
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ }
+
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ rv = conn_call_begin_headers(conn, stream);
+ break;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ rv = conn_call_begin_trailers(conn, stream);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ if (rv != 0) {
+ return rv;
+ }
+
+ rstate->state = NGHTTP3_REQ_STREAM_STATE_HEADERS;
+ break;
+ case NGHTTP3_FRAME_PUSH_PROMISE: /* We do not support push */
+ case NGHTTP3_FRAME_CANCEL_PUSH:
+ case NGHTTP3_FRAME_SETTINGS:
+ case NGHTTP3_FRAME_GOAWAY:
+ case NGHTTP3_FRAME_MAX_PUSH_ID:
+ case NGHTTP3_FRAME_PRIORITY_UPDATE:
+ case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID:
+ case NGHTTP3_H2_FRAME_PRIORITY:
+ case NGHTTP3_H2_FRAME_PING:
+ case NGHTTP3_H2_FRAME_WINDOW_UPDATE:
+ case NGHTTP3_H2_FRAME_CONTINUATION:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ default:
+ /* TODO Handle reserved frame type */
+ busy = 1;
+ rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_FRAME;
+ break;
+ }
+ break;
+ case NGHTTP3_REQ_STREAM_STATE_DATA:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ rv = nghttp3_conn_on_data(conn, stream, p, len);
+ if (rv != 0) {
+ return rv;
+ }
+ p += len;
+ rstate->left -= (int64_t)len;
+
+ if (rstate->left) {
+ goto almost_done;
+ }
+
+ rv = nghttp3_stream_transit_rx_http_state(stream,
+ NGHTTP3_HTTP_EVENT_DATA_END);
+ assert(0 == rv);
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_REQ_STREAM_STATE_HEADERS:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ nread = nghttp3_conn_on_headers(conn, stream, p, len,
+ (int64_t)len == rstate->left);
+ if (nread < 0) {
+ if (nread == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) {
+ goto http_header_error;
+ }
+
+ return nread;
+ }
+
+ p += nread;
+ nconsumed += (size_t)nread;
+ rstate->left -= nread;
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) {
+ if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) {
+ rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p));
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ *pnproc = (size_t)(p - src);
+ return (nghttp3_ssize)nconsumed;
+ }
+
+ if (rstate->left) {
+ goto almost_done;
+ }
+
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
+ rv = nghttp3_http_on_request_headers(&stream->rx.http);
+ break;
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ rv = nghttp3_http_on_response_headers(&stream->rx.http);
+ break;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ rv = 0;
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ if (rv != 0) {
+ if (rv == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) {
+ goto http_header_error;
+ }
+
+ return rv;
+ }
+
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
+ /* Only server utilizes priority information to schedule
+ streams. */
+ if (conn->server &&
+ (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_PRIORITY) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET)) {
+ rv = conn_update_stream_priority(conn, stream, stream->rx.http.pri);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ /* fall through */
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ rv = conn_call_end_headers(conn, stream, p == end && fin);
+ break;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ rv = conn_call_end_trailers(conn, stream, p == end && fin);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = nghttp3_stream_transit_rx_http_state(stream,
+ NGHTTP3_HTTP_EVENT_HEADERS_END);
+ assert(0 == rv);
+
+ nghttp3_stream_read_state_reset(rstate);
+
+ break;
+
+ http_header_error:
+ stream->flags |= NGHTTP3_STREAM_FLAG_HTTP_ERROR;
+
+ busy = 1;
+ rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_REST;
+
+ rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_MESSAGE_ERROR);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_call_reset_stream(conn, stream, NGHTTP3_H3_MESSAGE_ERROR);
+ if (rv != 0) {
+ return rv;
+ }
+
+ break;
+ case NGHTTP3_REQ_STREAM_STATE_IGN_FRAME:
+ len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p));
+ p += len;
+ nconsumed += len;
+ rstate->left -= (int64_t)len;
+
+ if (rstate->left) {
+ goto almost_done;
+ }
+
+ nghttp3_stream_read_state_reset(rstate);
+ break;
+ case NGHTTP3_REQ_STREAM_STATE_IGN_REST:
+ nconsumed += (size_t)(end - p);
+ *pnproc = (size_t)(end - src);
+ return (nghttp3_ssize)nconsumed;
+ }
+ }
+
+almost_done:
+ if (fin) {
+ switch (rstate->state) {
+ case NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE:
+ if (rvint->left) {
+ return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR;
+ }
+ rv = nghttp3_stream_transit_rx_http_state(stream,
+ NGHTTP3_HTTP_EVENT_MSG_END);
+ if (rv != 0) {
+ return rv;
+ }
+ rv = conn_call_end_stream(conn, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGHTTP3_REQ_STREAM_STATE_IGN_REST:
+ break;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_ERROR;
+ }
+ }
+
+ *pnproc = (size_t)(p - src);
+ return (nghttp3_ssize)nconsumed;
+}
+
+int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream,
+ const uint8_t *data, size_t datalen) {
+ int rv;
+
+ rv = nghttp3_http_on_data_chunk(stream, datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (!conn->callbacks.recv_data) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_data(conn, stream->node.id, data, datalen,
+ conn->user_data, stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static nghttp3_pq *conn_get_sched_pq(nghttp3_conn *conn, nghttp3_tnode *tnode) {
+ uint32_t urgency = nghttp3_pri_uint8_urgency(tnode->pri);
+
+ assert(urgency < NGHTTP3_URGENCY_LEVELS);
+
+ return &conn->sched[urgency].spq;
+}
+
+static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn,
+ nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen,
+ int fin) {
+ nghttp3_ssize nread;
+ int rv;
+ nghttp3_qpack_decoder *qdec = &conn->qdec;
+ nghttp3_qpack_nv nv;
+ uint8_t flags;
+ nghttp3_buf buf;
+ nghttp3_recv_header recv_header = NULL;
+ nghttp3_http_state *http;
+ int request = 0;
+ int trailers = 0;
+
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
+ request = 1;
+ /* Fall through */
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ recv_header = conn->callbacks.recv_header;
+ break;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ request = 1;
+ /* Fall through */
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ trailers = 1;
+ recv_header = conn->callbacks.recv_trailer;
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+ http = &stream->rx.http;
+
+ nghttp3_buf_wrap_init(&buf, (uint8_t *)src, srclen);
+ buf.last = buf.end;
+
+ for (;;) {
+ nread = nghttp3_qpack_decoder_read_request(qdec, &stream->qpack_sctx, &nv,
+ &flags, buf.pos,
+ nghttp3_buf_len(&buf), fin);
+
+ if (nread < 0) {
+ return (int)nread;
+ }
+
+ buf.pos += nread;
+
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_BLOCKED) {
+ if (conn->local.settings.qpack_blocked_streams <=
+ nghttp3_pq_size(&conn->qpack_blocked_streams)) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED;
+ rv = nghttp3_conn_qpack_blocked_streams_push(conn, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ }
+
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL) {
+ nghttp3_qpack_stream_context_reset(&stream->qpack_sctx);
+ break;
+ }
+
+ if (nread == 0) {
+ break;
+ }
+
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT) {
+ rv = nghttp3_http_on_header(
+ http, &nv, request, trailers,
+ conn->server && conn->local.settings.enable_connect_protocol);
+ switch (rv) {
+ case NGHTTP3_ERR_MALFORMED_HTTP_HEADER:
+ break;
+ case NGHTTP3_ERR_REMOVE_HTTP_HEADER:
+ rv = 0;
+ break;
+ case 0:
+ if (recv_header) {
+ rv = recv_header(conn, stream->node.id, nv.token, nv.name, nv.value,
+ nv.flags, conn->user_data, stream->user_data);
+ if (rv != 0) {
+ rv = NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ }
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ nghttp3_rcbuf_decref(nv.name);
+ nghttp3_rcbuf_decref(nv.value);
+
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
+
+ return buf.pos - src;
+}
+
+nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn,
+ nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen,
+ int fin) {
+ if (srclen == 0 && !fin) {
+ return 0;
+ }
+
+ return conn_decode_headers(conn, stream, src, srclen, fin);
+}
+
+int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn,
+ const nghttp3_frame_settings *fr) {
+ const nghttp3_settings_entry *ent = &fr->iv[0];
+ nghttp3_settings *dest = &conn->remote.settings;
+
+ /* TODO Check for duplicates */
+ switch (ent->id) {
+ case NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE:
+ dest->max_field_section_size = ent->value;
+ break;
+ case NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY:
+ if (dest->qpack_max_dtable_capacity != 0) {
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
+ }
+
+ if (ent->value == 0) {
+ break;
+ }
+
+ dest->qpack_max_dtable_capacity = (size_t)ent->value;
+
+ nghttp3_qpack_encoder_set_max_dtable_capacity(&conn->qenc,
+ (size_t)ent->value);
+ break;
+ case NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS:
+ if (dest->qpack_blocked_streams != 0) {
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
+ }
+
+ if (ent->value == 0) {
+ break;
+ }
+
+ dest->qpack_blocked_streams = (size_t)ent->value;
+
+ nghttp3_qpack_encoder_set_max_blocked_streams(
+ &conn->qenc, (size_t)nghttp3_min(100, ent->value));
+ break;
+ case NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL:
+ if (!conn->server) {
+ break;
+ }
+
+ switch (ent->value) {
+ case 0:
+ if (dest->enable_connect_protocol) {
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
+ }
+
+ break;
+ case 1:
+ break;
+ default:
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
+ }
+
+ dest->enable_connect_protocol = (int)ent->value;
+ break;
+ case NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH:
+ case NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS:
+ case NGHTTP3_H2_SETTINGS_ID_INITIAL_WINDOW_SIZE:
+ case NGHTTP3_H2_SETTINGS_ID_MAX_FRAME_SIZE:
+ return NGHTTP3_ERR_H3_SETTINGS_ERROR;
+ default:
+ /* Ignore unknown settings ID */
+ break;
+ }
+
+ return 0;
+}
+
+static int
+conn_on_priority_update_stream(nghttp3_conn *conn,
+ const nghttp3_frame_priority_update *fr) {
+ int64_t stream_id = fr->pri_elem_id;
+ nghttp3_stream *stream;
+ int rv;
+
+ if (!nghttp3_client_stream_bidi(stream_id) ||
+ nghttp3_ord_stream_id(stream_id) > conn->remote.bidi.max_client_streams) {
+ return NGHTTP3_ERR_H3_ID_ERROR;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) &&
+ conn->tx.goaway_id <= stream_id) {
+ /* Connection is going down. Ignore priority signal. */
+ return 0;
+ }
+
+ rv = conn_bidi_idtr_open(conn, stream_id);
+ if (rv != 0) {
+ if (nghttp3_err_is_fatal(rv)) {
+ return rv;
+ }
+
+ assert(rv == NGHTTP3_ERR_STREAM_IN_USE);
+
+ /* The stream is gone. Just ignore. */
+ return 0;
+ }
+
+ conn->rx.max_stream_id_bidi =
+ nghttp3_max(conn->rx.max_stream_id_bidi, stream_id);
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->node.pri = nghttp3_pri_to_uint8(&fr->pri);
+ stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED;
+
+ return 0;
+ }
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET) {
+ return 0;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED;
+
+ return conn_update_stream_priority(conn, stream,
+ nghttp3_pri_to_uint8(&fr->pri));
+}
+
+int nghttp3_conn_on_priority_update(nghttp3_conn *conn,
+ const nghttp3_frame_priority_update *fr) {
+ assert(conn->server);
+ assert(fr->hd.type == NGHTTP3_FRAME_PRIORITY_UPDATE);
+
+ return conn_on_priority_update_stream(conn, fr);
+}
+
+static int conn_stream_acked_data(nghttp3_stream *stream, int64_t stream_id,
+ uint64_t datalen, void *user_data) {
+ nghttp3_conn *conn = stream->conn;
+ int rv;
+
+ if (!conn->callbacks.acked_stream_data) {
+ return 0;
+ }
+
+ rv = conn->callbacks.acked_stream_data(conn, stream_id, datalen,
+ conn->user_data, user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream,
+ int64_t stream_id) {
+ nghttp3_stream *stream;
+ int rv;
+ nghttp3_stream_callbacks callbacks = {
+ conn_stream_acked_data,
+ };
+
+ rv = nghttp3_stream_new(&stream, stream_id, &callbacks,
+ &conn->out_chunk_objalloc, &conn->stream_objalloc,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->conn = conn;
+
+ rv = nghttp3_map_insert(&conn->streams, (nghttp3_map_key_type)stream->node.id,
+ stream);
+ if (rv != 0) {
+ nghttp3_stream_del(stream);
+ return rv;
+ }
+
+ *pstream = stream;
+
+ return 0;
+}
+
+nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn,
+ int64_t stream_id) {
+ return nghttp3_map_find(&conn->streams, (nghttp3_map_key_type)stream_id);
+}
+
+int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream;
+ nghttp3_frame_entry frent;
+ int rv;
+
+ assert(!conn->server || nghttp3_server_stream_uni(stream_id));
+ assert(conn->server || nghttp3_client_stream_uni(stream_id));
+
+ if (conn->tx.ctrl) {
+ return NGHTTP3_ERR_INVALID_STATE;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->type = NGHTTP3_STREAM_TYPE_CONTROL;
+
+ conn->tx.ctrl = stream;
+
+ rv = nghttp3_stream_write_stream_type(stream);
+ if (rv != 0) {
+ return rv;
+ }
+
+ frent.fr.hd.type = NGHTTP3_FRAME_SETTINGS;
+ frent.aux.settings.local_settings = &conn->local.settings;
+
+ return nghttp3_stream_frq_add(stream, &frent);
+}
+
+int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, int64_t qenc_stream_id,
+ int64_t qdec_stream_id) {
+ nghttp3_stream *stream;
+ int rv;
+
+ assert(!conn->server || nghttp3_server_stream_uni(qenc_stream_id));
+ assert(!conn->server || nghttp3_server_stream_uni(qdec_stream_id));
+ assert(conn->server || nghttp3_client_stream_uni(qenc_stream_id));
+ assert(conn->server || nghttp3_client_stream_uni(qdec_stream_id));
+
+ if (conn->tx.qenc || conn->tx.qdec) {
+ return NGHTTP3_ERR_INVALID_STATE;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, qenc_stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->type = NGHTTP3_STREAM_TYPE_QPACK_ENCODER;
+
+ conn->tx.qenc = stream;
+
+ rv = nghttp3_stream_write_stream_type(stream);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, qdec_stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->type = NGHTTP3_STREAM_TYPE_QPACK_DECODER;
+
+ conn->tx.qdec = stream;
+
+ return nghttp3_stream_write_stream_type(stream);
+}
+
+static nghttp3_ssize conn_writev_stream(nghttp3_conn *conn, int64_t *pstream_id,
+ int *pfin, nghttp3_vec *vec,
+ size_t veccnt, nghttp3_stream *stream) {
+ int rv;
+ nghttp3_ssize n;
+
+ assert(veccnt > 0);
+
+ /* If stream is blocked by read callback, don't attempt to fill
+ more. */
+ if (!(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED)) {
+ rv = nghttp3_stream_fill_outq(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (!nghttp3_stream_uni(stream->node.id) && conn->tx.qenc &&
+ !nghttp3_stream_is_blocked(conn->tx.qenc)) {
+ n = nghttp3_stream_writev(conn->tx.qenc, pfin, vec, veccnt);
+ if (n < 0) {
+ return n;
+ }
+ if (n) {
+ *pstream_id = conn->tx.qenc->node.id;
+ return n;
+ }
+ }
+
+ n = nghttp3_stream_writev(stream, pfin, vec, veccnt);
+ if (n < 0) {
+ return n;
+ }
+ /* We might just want to write stream fin without sending any stream
+ data. */
+ if (n == 0 && *pfin == 0) {
+ return 0;
+ }
+
+ *pstream_id = stream->node.id;
+
+ return n;
+}
+
+nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn,
+ int64_t *pstream_id, int *pfin,
+ nghttp3_vec *vec, size_t veccnt) {
+ nghttp3_ssize ncnt;
+ nghttp3_stream *stream;
+ int rv;
+
+ *pstream_id = -1;
+ *pfin = 0;
+
+ if (veccnt == 0) {
+ return 0;
+ }
+
+ if (conn->tx.ctrl && !nghttp3_stream_is_blocked(conn->tx.ctrl)) {
+ ncnt =
+ conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.ctrl);
+ if (ncnt) {
+ return ncnt;
+ }
+ }
+
+ if (conn->tx.qdec && !nghttp3_stream_is_blocked(conn->tx.qdec)) {
+ rv = nghttp3_stream_write_qpack_decoder_stream(conn->tx.qdec);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ncnt =
+ conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.qdec);
+ if (ncnt) {
+ return ncnt;
+ }
+ }
+
+ if (conn->tx.qenc && !nghttp3_stream_is_blocked(conn->tx.qenc)) {
+ ncnt =
+ conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.qenc);
+ if (ncnt) {
+ return ncnt;
+ }
+ }
+
+ stream = nghttp3_conn_get_next_tx_stream(conn);
+ if (stream == NULL) {
+ return 0;
+ }
+
+ ncnt = conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, stream);
+ if (ncnt < 0) {
+ return ncnt;
+ }
+
+ if (nghttp3_client_stream_bidi(stream->node.id) &&
+ !nghttp3_stream_require_schedule(stream)) {
+ nghttp3_conn_unschedule_stream(conn, stream);
+ }
+
+ return ncnt;
+}
+
+nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn) {
+ size_t i;
+ nghttp3_tnode *tnode;
+ nghttp3_pq *pq;
+
+ for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) {
+ pq = &conn->sched[i].spq;
+ if (nghttp3_pq_empty(pq)) {
+ continue;
+ }
+
+ tnode = nghttp3_struct_of(nghttp3_pq_top(pq), nghttp3_tnode, pe);
+
+ return nghttp3_struct_of(tnode, nghttp3_stream, node);
+ }
+
+ return NULL;
+}
+
+int nghttp3_conn_add_write_offset(nghttp3_conn *conn, int64_t stream_id,
+ size_t n) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+ int rv;
+
+ if (stream == NULL) {
+ return 0;
+ }
+
+ rv = nghttp3_stream_add_outq_offset(stream, n);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->unscheduled_nwrite += n;
+
+ if (!nghttp3_client_stream_bidi(stream->node.id)) {
+ return 0;
+ }
+
+ if (!nghttp3_stream_require_schedule(stream)) {
+ nghttp3_conn_unschedule_stream(conn, stream);
+ return 0;
+ }
+
+ if (stream->unscheduled_nwrite < NGHTTP3_STREAM_MIN_WRITELEN) {
+ return 0;
+ }
+
+ return nghttp3_conn_schedule_stream(conn, stream);
+}
+
+int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t n) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return 0;
+ }
+
+ return nghttp3_stream_add_ack_offset(stream, n);
+}
+
+static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream,
+ const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_data_reader *dr) {
+ int rv;
+ nghttp3_nv *nnva;
+ nghttp3_frame_entry frent;
+
+ rv = nghttp3_nva_copy(&nnva, nva, nvlen, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ frent.fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ frent.fr.headers.nva = nnva;
+ frent.fr.headers.nvlen = nvlen;
+
+ rv = nghttp3_stream_frq_add(stream, &frent);
+ if (rv != 0) {
+ nghttp3_nva_del(nnva, conn->mem);
+ return rv;
+ }
+
+ if (dr) {
+ frent.fr.hd.type = NGHTTP3_FRAME_DATA;
+ frent.aux.data.dr = *dr;
+
+ rv = nghttp3_stream_frq_add(stream, &frent);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (nghttp3_stream_require_schedule(stream)) {
+ return nghttp3_conn_schedule_stream(conn, stream);
+ }
+
+ return 0;
+}
+
+int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
+ /* Assume that stream stays on the same urgency level */
+ nghttp3_tnode *node = stream_get_sched_node(stream);
+ int rv;
+
+ rv = nghttp3_tnode_schedule(node, conn_get_sched_pq(conn, node),
+ stream->unscheduled_nwrite);
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream->unscheduled_nwrite = 0;
+
+ return 0;
+}
+
+int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn,
+ nghttp3_stream *stream) {
+ if (nghttp3_tnode_is_scheduled(stream_get_sched_node(stream))) {
+ return 0;
+ }
+
+ return nghttp3_conn_schedule_stream(conn, stream);
+}
+
+void nghttp3_conn_unschedule_stream(nghttp3_conn *conn,
+ nghttp3_stream *stream) {
+ nghttp3_tnode *node = stream_get_sched_node(stream);
+
+ nghttp3_tnode_unschedule(node, conn_get_sched_pq(conn, node));
+}
+
+int nghttp3_conn_submit_request(nghttp3_conn *conn, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_data_reader *dr,
+ void *stream_user_data) {
+ nghttp3_stream *stream;
+ int rv;
+
+ assert(!conn->server);
+ assert(conn->tx.qenc);
+
+ assert(nghttp3_client_stream_bidi(stream_id));
+
+ /* TODO Should we check that stream_id is client stream_id? */
+ /* TODO Check GOAWAY last stream ID */
+ if (nghttp3_stream_uni(stream_id)) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ if (conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_RECVED) {
+ return NGHTTP3_ERR_CONN_CLOSING;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream != NULL) {
+ return NGHTTP3_ERR_STREAM_IN_USE;
+ }
+
+ rv = nghttp3_conn_create_stream(conn, &stream, stream_id);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
+ stream->user_data = stream_user_data;
+
+ nghttp3_http_record_request_method(stream, nva, nvlen);
+
+ if (dr == NULL) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM;
+ }
+
+ return conn_submit_headers_data(conn, stream, nva, nvlen, dr);
+}
+
+int nghttp3_conn_submit_info(nghttp3_conn *conn, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen) {
+ nghttp3_stream *stream;
+
+ /* TODO Verify that it is allowed to send info (non-final response)
+ now. */
+ assert(conn->server);
+ assert(conn->tx.qenc);
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ return conn_submit_headers_data(conn, stream, nva, nvlen, NULL);
+}
+
+int nghttp3_conn_submit_response(nghttp3_conn *conn, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_data_reader *dr) {
+ nghttp3_stream *stream;
+
+ /* TODO Verify that it is allowed to send response now. */
+ assert(conn->server);
+ assert(conn->tx.qenc);
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ if (dr == NULL) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM;
+ }
+
+ return conn_submit_headers_data(conn, stream, nva, nvlen, dr);
+}
+
+int nghttp3_conn_submit_trailers(nghttp3_conn *conn, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen) {
+ nghttp3_stream *stream;
+
+ /* TODO Verify that it is allowed to send trailer now. */
+ assert(conn->tx.qenc);
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ if (stream->flags & NGHTTP3_STREAM_FLAG_WRITE_END_STREAM) {
+ return NGHTTP3_ERR_INVALID_STATE;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM;
+
+ return conn_submit_headers_data(conn, stream, nva, nvlen, NULL);
+}
+
+int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) {
+ nghttp3_frame_entry frent;
+ int rv;
+
+ assert(conn->tx.ctrl);
+
+ frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY;
+ frent.fr.goaway.id = conn->server ? NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID
+ : NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID;
+
+ assert(frent.fr.goaway.id <= conn->tx.goaway_id);
+
+ rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn->tx.goaway_id = frent.fr.goaway.id;
+ conn->flags |= NGHTTP3_CONN_FLAG_GOAWAY_QUEUED;
+
+ return 0;
+}
+
+int nghttp3_conn_shutdown(nghttp3_conn *conn) {
+ nghttp3_frame_entry frent;
+ int rv;
+
+ assert(conn->tx.ctrl);
+
+ frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY;
+ if (conn->server) {
+ frent.fr.goaway.id =
+ nghttp3_min((1ll << 62) - 4, conn->rx.max_stream_id_bidi + 4);
+ } else {
+ frent.fr.goaway.id = 0;
+ }
+
+ assert(frent.fr.goaway.id <= conn->tx.goaway_id);
+
+ rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn->tx.goaway_id = frent.fr.goaway.id;
+ conn->flags |= NGHTTP3_CONN_FLAG_GOAWAY_QUEUED;
+
+ return 0;
+}
+
+int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream) {
+ int rv;
+
+ rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_REQUEST_REJECTED);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return conn_call_reset_stream(conn, stream, NGHTTP3_H3_REQUEST_REJECTED);
+}
+
+void nghttp3_conn_block_stream(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_FC_BLOCKED;
+ stream->unscheduled_nwrite = 0;
+
+ if (nghttp3_client_stream_bidi(stream->node.id)) {
+ nghttp3_conn_unschedule_stream(conn, stream);
+ }
+}
+
+void nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_SHUT_WR;
+ stream->unscheduled_nwrite = 0;
+
+ if (nghttp3_client_stream_bidi(stream->node.id)) {
+ nghttp3_conn_unschedule_stream(conn, stream);
+ }
+}
+
+int nghttp3_conn_unblock_stream(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return 0;
+ }
+
+ stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_FC_BLOCKED;
+
+ if (nghttp3_client_stream_bidi(stream->node.id) &&
+ nghttp3_stream_require_schedule(stream)) {
+ return nghttp3_conn_ensure_stream_scheduled(conn, stream);
+ }
+
+ return 0;
+}
+
+int nghttp3_conn_is_stream_writable(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return 0;
+ }
+
+ return (stream->flags &
+ (NGHTTP3_STREAM_FLAG_FC_BLOCKED |
+ NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED | NGHTTP3_STREAM_FLAG_SHUT_WR |
+ NGHTTP3_STREAM_FLAG_CLOSED)) == 0;
+}
+
+int nghttp3_conn_resume_stream(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return 0;
+ }
+
+ stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED;
+
+ if (nghttp3_client_stream_bidi(stream->node.id) &&
+ nghttp3_stream_require_schedule(stream)) {
+ return nghttp3_conn_ensure_stream_scheduled(conn, stream);
+ }
+
+ return 0;
+}
+
+int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ if (nghttp3_stream_uni(stream_id) &&
+ stream->type != NGHTTP3_STREAM_TYPE_UNKNOWN) {
+ return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM;
+ }
+
+ stream->error_code = app_error_code;
+
+ nghttp3_conn_unschedule_stream(conn, stream);
+
+ if (stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX) {
+ return conn_delete_stream(conn, stream);
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_CLOSED;
+ return 0;
+}
+
+int nghttp3_conn_shutdown_stream_read(nghttp3_conn *conn, int64_t stream_id) {
+ nghttp3_stream *stream;
+
+ if (!nghttp3_client_stream_bidi(stream_id)) {
+ return 0;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream) {
+ if (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_RD) {
+ return 0;
+ }
+
+ stream->flags |= NGHTTP3_STREAM_FLAG_SHUT_RD;
+ }
+
+ return nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream_id);
+}
+
+int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn,
+ nghttp3_stream *stream) {
+ assert(stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX);
+
+ return nghttp3_pq_push(&conn->qpack_blocked_streams,
+ &stream->qpack_blocked_pe);
+}
+
+void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn) {
+ assert(!nghttp3_pq_empty(&conn->qpack_blocked_streams));
+ nghttp3_pq_pop(&conn->qpack_blocked_streams);
+}
+
+void nghttp3_conn_set_max_client_streams_bidi(nghttp3_conn *conn,
+ uint64_t max_streams) {
+ assert(conn->server);
+ assert(conn->remote.bidi.max_client_streams <= max_streams);
+
+ conn->remote.bidi.max_client_streams = max_streams;
+}
+
+void nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn,
+ size_t max_concurrent_streams) {
+ nghttp3_qpack_decoder_set_max_concurrent_streams(&conn->qdec,
+ max_concurrent_streams);
+}
+
+int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, int64_t stream_id,
+ void *stream_user_data) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ stream->user_data = stream_user_data;
+
+ return 0;
+}
+
+uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn,
+ int64_t stream_id) {
+ nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id);
+
+ if (stream == NULL) {
+ return 0;
+ }
+
+ return (uint64_t)stream->rstate.left;
+}
+
+int nghttp3_conn_get_stream_priority(nghttp3_conn *conn, nghttp3_pri *dest,
+ int64_t stream_id) {
+ nghttp3_stream *stream;
+
+ assert(conn->server);
+
+ if (!nghttp3_client_stream_bidi(stream_id)) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ dest->urgency = nghttp3_pri_uint8_urgency(stream->node.pri);
+ dest->inc = nghttp3_pri_uint8_inc(stream->node.pri);
+
+ return 0;
+}
+
+int nghttp3_conn_set_stream_priority(nghttp3_conn *conn, int64_t stream_id,
+ const nghttp3_pri *pri) {
+ nghttp3_stream *stream;
+ nghttp3_frame_entry frent;
+
+ assert(pri->urgency < NGHTTP3_URGENCY_LEVELS);
+ assert(pri->inc == 0 || pri->inc == 1);
+
+ if (!nghttp3_client_stream_bidi(stream_id)) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ if (stream == NULL) {
+ return NGHTTP3_ERR_STREAM_NOT_FOUND;
+ }
+
+ if (conn->server) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET;
+
+ return conn_update_stream_priority(conn, stream, nghttp3_pri_to_uint8(pri));
+ }
+
+ frent.fr.hd.type = NGHTTP3_FRAME_PRIORITY_UPDATE;
+ frent.fr.priority_update.pri_elem_id = stream_id;
+ frent.fr.priority_update.pri = *pri;
+
+ return nghttp3_stream_frq_add(conn->tx.ctrl, &frent);
+}
+
+int nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn,
+ int64_t stream_id) {
+ nghttp3_stream *stream;
+
+ if (!conn_remote_stream_uni(conn, stream_id)) {
+ return 0;
+ }
+
+ stream = nghttp3_conn_find_stream(conn, stream_id);
+ return stream && stream->type == NGHTTP3_STREAM_TYPE_QPACK_ENCODER;
+}
+
+void nghttp3_settings_default_versioned(int settings_version,
+ nghttp3_settings *settings) {
+ (void)settings_version;
+
+ memset(settings, 0, sizeof(nghttp3_settings));
+ settings->max_field_section_size = NGHTTP3_VARINT_MAX;
+ settings->qpack_encoder_max_dtable_capacity =
+ NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY;
+}
diff --git a/lib/nghttp3_conn.h b/lib/nghttp3_conn.h
new file mode 100644
index 0000000..a3f904c
--- /dev/null
+++ b/lib/nghttp3_conn.h
@@ -0,0 +1,200 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_CONN_H
+#define NGHTTP3_CONN_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_stream.h"
+#include "nghttp3_map.h"
+#include "nghttp3_qpack.h"
+#include "nghttp3_tnode.h"
+#include "nghttp3_idtr.h"
+#include "nghttp3_gaptr.h"
+
+#define NGHTTP3_VARINT_MAX ((1ull << 62) - 1)
+
+/* NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY is the maximum dynamic
+ table size for QPACK encoder. */
+#define NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY 16384
+
+/* NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS is the maximum number of
+ blocked streams for QPACK encoder. */
+#define NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS 100
+
+/* NGHTTP3_CONN_FLAG_NONE indicates that no flag is set. */
+#define NGHTTP3_CONN_FLAG_NONE 0x0000u
+/* NGHTTP3_CONN_FLAG_SETTINGS_RECVED is set when SETTINGS frame has
+ been received. */
+#define NGHTTP3_CONN_FLAG_SETTINGS_RECVED 0x0001u
+/* NGHTTP3_CONN_FLAG_CONTROL_OPENED is set when a control stream has
+ opened. */
+#define NGHTTP3_CONN_FLAG_CONTROL_OPENED 0x0002u
+/* NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED is set when a QPACK encoder
+ stream has opened. */
+#define NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED 0x0004u
+/* NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED is set when a QPACK decoder
+ stream has opened. */
+#define NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED 0x0008u
+/* NGHTTP3_CONN_FLAG_GOAWAY_RECVED indicates that GOAWAY frame has
+ received. */
+#define NGHTTP3_CONN_FLAG_GOAWAY_RECVED 0x0020u
+/* NGHTTP3_CONN_FLAG_GOAWAY_QUEUED indicates that GOAWAY frame has
+ been submitted for transmission. */
+#define NGHTTP3_CONN_FLAG_GOAWAY_QUEUED 0x0040u
+
+typedef struct nghttp3_chunk {
+ nghttp3_opl_entry oplent;
+} nghttp3_chunk;
+
+nghttp3_objalloc_def(chunk, nghttp3_chunk, oplent);
+
+struct nghttp3_conn {
+ nghttp3_objalloc out_chunk_objalloc;
+ nghttp3_objalloc stream_objalloc;
+ nghttp3_callbacks callbacks;
+ nghttp3_map streams;
+ nghttp3_qpack_decoder qdec;
+ nghttp3_qpack_encoder qenc;
+ nghttp3_pq qpack_blocked_streams;
+ struct {
+ nghttp3_pq spq;
+ } sched[NGHTTP3_URGENCY_LEVELS];
+ const nghttp3_mem *mem;
+ void *user_data;
+ int server;
+ uint16_t flags;
+
+ struct {
+ nghttp3_settings settings;
+ struct {
+ /* max_pushes is the number of push IDs that local endpoint can
+ issue. This field is used by server only and used just for
+ validation */
+ uint64_t max_pushes;
+ } uni;
+ } local;
+
+ struct {
+ struct {
+ nghttp3_idtr idtr;
+ /* max_client_streams is the cumulative number of client
+ initiated bidirectional stream ID the remote endpoint can
+ issue. This field is used on server side only. */
+ uint64_t max_client_streams;
+ } bidi;
+ nghttp3_settings settings;
+ } remote;
+
+ struct {
+ /* goaway_id is the latest ID received in GOAWAY frame. */
+ int64_t goaway_id;
+
+ int64_t max_stream_id_bidi;
+
+ /* pri_fieldbuf is a buffer to store incoming Priority Field Value
+ in PRIORITY_UPDATE frame. */
+ uint8_t pri_fieldbuf[8];
+ /* pri_fieldlen is the number of bytes written into
+ pri_fieldbuf. */
+ size_t pri_fieldbuflen;
+ } rx;
+
+ struct {
+ struct {
+ nghttp3_buf rbuf;
+ nghttp3_buf ebuf;
+ } qpack;
+ nghttp3_stream *ctrl;
+ nghttp3_stream *qenc;
+ nghttp3_stream *qdec;
+ /* goaway_id is the latest ID sent in GOAWAY frame. */
+ int64_t goaway_id;
+ } tx;
+};
+
+nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, int64_t stream_id);
+
+int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream,
+ int64_t stream_id);
+
+nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
+ nghttp3_stream *stream, const uint8_t *src,
+ size_t srclen, int fin);
+
+nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen, int fin);
+
+nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
+ nghttp3_stream *stream,
+ const uint8_t *src, size_t srclen);
+
+nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn,
+ const uint8_t *src,
+ size_t srclen);
+
+nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn,
+ const uint8_t *src,
+ size_t srclen);
+
+int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream,
+ const uint8_t *data, size_t datalen);
+
+int nghttp3_conn_on_priority_update(nghttp3_conn *conn,
+ const nghttp3_frame_priority_update *fr);
+
+nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn,
+ nghttp3_stream *stream,
+ const uint8_t *data, size_t datalen,
+ int fin);
+
+int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn,
+ const nghttp3_frame_settings *fr);
+
+int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn,
+ nghttp3_stream *stream);
+
+void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn);
+
+int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream);
+
+int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn,
+ nghttp3_stream *stream);
+
+void nghttp3_conn_unschedule_stream(nghttp3_conn *conn, nghttp3_stream *stream);
+
+int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream);
+
+/*
+ * nghttp3_conn_get_next_tx_stream returns next stream to send. It
+ * returns NULL if there is no such stream.
+ */
+nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn);
+
+#endif /* NGHTTP3_CONN_H */
diff --git a/lib/nghttp3_conv.c b/lib/nghttp3_conv.c
new file mode 100644
index 0000000..f7853d9
--- /dev/null
+++ b/lib/nghttp3_conv.c
@@ -0,0 +1,125 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_conv.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "nghttp3_str.h"
+#include "nghttp3_unreachable.h"
+
+int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p) {
+ union {
+ char b[8];
+ uint16_t n16;
+ uint32_t n32;
+ uint64_t n64;
+ } n;
+
+ *plen = 1u << (*p >> 6);
+
+ switch (*plen) {
+ case 1:
+ return (int64_t)*p;
+ case 2:
+ memcpy(&n, p, 2);
+ n.b[0] &= 0x3f;
+ return (int64_t)ntohs(n.n16);
+ case 4:
+ memcpy(&n, p, 4);
+ n.b[0] &= 0x3f;
+ return (int64_t)ntohl(n.n32);
+ case 8:
+ memcpy(&n, p, 8);
+ n.b[0] &= 0x3f;
+ return (int64_t)nghttp3_ntohl64(n.n64);
+ }
+
+ nghttp3_unreachable();
+}
+
+int64_t nghttp3_get_varint_fb(const uint8_t *p) { return *p & 0x3f; }
+
+size_t nghttp3_get_varintlen(const uint8_t *p) { return 1u << (*p >> 6); }
+
+uint8_t *nghttp3_put_uint64be(uint8_t *p, uint64_t n) {
+ n = nghttp3_htonl64(n);
+ return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *nghttp3_put_uint32be(uint8_t *p, uint32_t n) {
+ n = htonl(n);
+ return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *nghttp3_put_uint16be(uint8_t *p, uint16_t n) {
+ n = htons(n);
+ return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *nghttp3_put_varint(uint8_t *p, int64_t n) {
+ uint8_t *rv;
+ if (n < 64) {
+ *p++ = (uint8_t)n;
+ return p;
+ }
+ if (n < 16384) {
+ rv = nghttp3_put_uint16be(p, (uint16_t)n);
+ *p |= 0x40;
+ return rv;
+ }
+ if (n < 1073741824) {
+ rv = nghttp3_put_uint32be(p, (uint32_t)n);
+ *p |= 0x80;
+ return rv;
+ }
+ assert(n < 4611686018427387904LL);
+ rv = nghttp3_put_uint64be(p, (uint64_t)n);
+ *p |= 0xc0;
+ return rv;
+}
+
+size_t nghttp3_put_varintlen(int64_t n) {
+ if (n < 64) {
+ return 1;
+ }
+ if (n < 16384) {
+ return 2;
+ }
+ if (n < 1073741824) {
+ return 4;
+ }
+ assert(n < 4611686018427387904LL);
+ return 8;
+}
+
+uint64_t nghttp3_ord_stream_id(int64_t stream_id) {
+ return (uint64_t)(stream_id >> 2) + 1;
+}
+
+uint8_t nghttp3_pri_to_uint8(const nghttp3_pri *pri) {
+ return (uint8_t)((uint32_t)pri->inc << 7 | pri->urgency);
+}
diff --git a/lib/nghttp3_conv.h b/lib/nghttp3_conv.h
new file mode 100644
index 0000000..516013a
--- /dev/null
+++ b/lib/nghttp3_conv.h
@@ -0,0 +1,207 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_CONV_H
+#define NGHTTP3_CONV_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif /* HAVE_ARPA_INET_H */
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+#ifdef HAVE_BYTESWAP_H
+# include <byteswap.h>
+#endif /* HAVE_BYTESWAP_H */
+
+#ifdef HAVE_ENDIAN_H
+# include <endian.h>
+#endif /* HAVE_ENDIAN_H */
+
+#ifdef HAVE_SYS_ENDIAN_H
+# include <sys/endian.h>
+#endif /* HAVE_SYS_ENDIAN_H */
+
+#include <nghttp3/nghttp3.h>
+
+#if defined(HAVE_BSWAP_64) || \
+ (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0)
+# define nghttp3_bswap64 bswap_64
+#else /* !HAVE_BSWAP_64 */
+# define nghttp3_bswap64(N) \
+ ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32)))
+#endif /* !HAVE_BSWAP_64 */
+
+#if defined(HAVE_BE64TOH) || \
+ (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0)
+# define nghttp3_ntohl64(N) be64toh(N)
+# define nghttp3_htonl64(N) htobe64(N)
+#else /* !HAVE_BE64TOH */
+# if defined(WORDS_BIGENDIAN)
+# define nghttp3_ntohl64(N) (N)
+# define nghttp3_htonl64(N) (N)
+# else /* !WORDS_BIGENDIAN */
+# define nghttp3_ntohl64(N) nghttp3_bswap64(N)
+# define nghttp3_htonl64(N) nghttp3_bswap64(N)
+# endif /* !WORDS_BIGENDIAN */
+#endif /* !HAVE_BE64TOH */
+
+#if defined(WIN32)
+/* Windows requires ws2_32 library for ntonl family of functions. We
+ define inline functions for those functions so that we don't have
+ dependency on that lib. */
+
+# ifdef _MSC_VER
+# define STIN static __inline
+# else
+# define STIN static inline
+# endif
+
+STIN uint32_t htonl(uint32_t hostlong) {
+ uint32_t res;
+ unsigned char *p = (unsigned char *)&res;
+ *p++ = (unsigned char)(hostlong >> 24);
+ *p++ = (hostlong >> 16) & 0xffu;
+ *p++ = (hostlong >> 8) & 0xffu;
+ *p = hostlong & 0xffu;
+ return res;
+}
+
+STIN uint16_t htons(uint16_t hostshort) {
+ uint16_t res;
+ unsigned char *p = (unsigned char *)&res;
+ *p++ = (unsigned char)(hostshort >> 8);
+ *p = hostshort & 0xffu;
+ return res;
+}
+
+STIN uint32_t ntohl(uint32_t netlong) {
+ uint32_t res;
+ unsigned char *p = (unsigned char *)&netlong;
+ res = *p++ << 24;
+ res += *p++ << 16;
+ res += *p++ << 8;
+ res += *p;
+ return res;
+}
+
+STIN uint16_t ntohs(uint16_t netshort) {
+ uint16_t res;
+ unsigned char *p = (unsigned char *)&netshort;
+ res = *p++ << 8;
+ res += *p;
+ return res;
+}
+
+#endif /* WIN32 */
+
+/*
+ * nghttp3_get_varint reads variable-length integer from |p|, and
+ * returns it in host byte order. The number of bytes read is stored
+ * in |*plen|.
+ */
+int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p);
+
+/*
+ * nghttp3_get_varint_fb reads first byte of encoded variable-length
+ * integer from |p|.
+ */
+int64_t nghttp3_get_varint_fb(const uint8_t *p);
+
+/*
+ * nghttp3_get_varintlen returns the required number of bytes to read
+ * variable-length integer starting at |p|.
+ */
+size_t nghttp3_get_varintlen(const uint8_t *p);
+
+/*
+ * nghttp3_put_uint64be writes |n| in host byte order in |p| in
+ * network byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *nghttp3_put_uint64be(uint8_t *p, uint64_t n);
+
+/*
+ * nghttp3_put_uint32be writes |n| in host byte order in |p| in
+ * network byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *nghttp3_put_uint32be(uint8_t *p, uint32_t n);
+
+/*
+ * nghttp3_put_uint16be writes |n| in host byte order in |p| in
+ * network byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *nghttp3_put_uint16be(uint8_t *p, uint16_t n);
+
+/*
+ * nghttp3_put_varint writes |n| in |p| using variable-length integer
+ * encoding. It returns the one beyond of the last written position.
+ */
+uint8_t *nghttp3_put_varint(uint8_t *p, int64_t n);
+
+/*
+ * nghttp3_put_varintlen returns the required number of bytes to
+ * encode |n|.
+ */
+size_t nghttp3_put_varintlen(int64_t n);
+
+/*
+ * nghttp3_ord_stream_id returns the ordinal number of |stream_id|.
+ */
+uint64_t nghttp3_ord_stream_id(int64_t stream_id);
+
+/*
+ * NGHTTP3_PRI_INC_MASK is a bit mask to retrieve incremental bit from
+ * a value produced by nghttp3_pri_to_uint8.
+ */
+#define NGHTTP3_PRI_INC_MASK (1 << 7)
+
+/*
+ * nghttp3_pri_to_uint8 encodes |pri| into uint8_t variable.
+ */
+uint8_t nghttp3_pri_to_uint8(const nghttp3_pri *pri);
+
+/*
+ * nghttp3_pri_uint8_urgency extracts urgency from |PRI| which is
+ * supposed to be constructed by nghttp3_pri_to_uint8.
+ */
+#define nghttp3_pri_uint8_urgency(PRI) \
+ ((uint32_t)((PRI) & ~NGHTTP3_PRI_INC_MASK))
+
+/*
+ * nghttp3_pri_uint8_inc extracts inc from |PRI| which is supposed to
+ * be constructed by nghttp3_pri_to_uint8.
+ */
+#define nghttp3_pri_uint8_inc(PRI) (((PRI)&NGHTTP3_PRI_INC_MASK) != 0)
+
+#endif /* NGHTTP3_CONV_H */
diff --git a/lib/nghttp3_debug.c b/lib/nghttp3_debug.c
new file mode 100644
index 0000000..4021b0d
--- /dev/null
+++ b/lib/nghttp3_debug.c
@@ -0,0 +1,61 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_debug.h"
+
+#include <stdio.h>
+
+#ifdef DEBUGBUILD
+
+static void nghttp3_default_debug_vfprintf_callback(const char *fmt,
+ va_list args) {
+ vfprintf(stderr, fmt, args);
+}
+
+static nghttp3_debug_vprintf_callback static_debug_vprintf_callback =
+ nghttp3_default_debug_vfprintf_callback;
+
+void nghttp3_debug_vprintf(const char *format, ...) {
+ if (static_debug_vprintf_callback) {
+ va_list args;
+ va_start(args, format);
+ static_debug_vprintf_callback(format, args);
+ va_end(args);
+ }
+}
+
+void nghttp3_set_debug_vprintf_callback(
+ nghttp3_debug_vprintf_callback debug_vprintf_callback) {
+ static_debug_vprintf_callback = debug_vprintf_callback;
+}
+
+#else /* !DEBUGBUILD */
+
+void nghttp3_set_debug_vprintf_callback(
+ nghttp3_debug_vprintf_callback debug_vprintf_callback) {
+ (void)debug_vprintf_callback;
+}
+
+#endif /* !DEBUGBUILD */
diff --git a/lib/nghttp3_debug.h b/lib/nghttp3_debug.h
new file mode 100644
index 0000000..01ed918
--- /dev/null
+++ b/lib/nghttp3_debug.h
@@ -0,0 +1,44 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_DEBUG_H
+#define NGHTTP3_DEBUG_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#ifdef DEBUGBUILD
+# define DEBUGF(...) nghttp3_debug_vprintf(__VA_ARGS__)
+void nghttp3_debug_vprintf(const char *format, ...);
+#else
+# define DEBUGF(...) \
+ do { \
+ } while (0)
+#endif
+
+#endif /* NGHTTP3_DEBUG_H */
diff --git a/lib/nghttp3_err.c b/lib/nghttp3_err.c
new file mode 100644
index 0000000..5cf94db
--- /dev/null
+++ b/lib/nghttp3_err.c
@@ -0,0 +1,126 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_err.h"
+
+const char *nghttp3_strerror(int liberr) {
+ switch (liberr) {
+ case NGHTTP3_ERR_INVALID_ARGUMENT:
+ return "ERR_INVALID_ARGUMENT";
+ case NGHTTP3_ERR_NOBUF:
+ return "ERR_NOBUF";
+ case NGHTTP3_ERR_INVALID_STATE:
+ return "ERR_INVALID_STATE";
+ case NGHTTP3_ERR_WOULDBLOCK:
+ return "ERR_WOULDBLOCK";
+ case NGHTTP3_ERR_STREAM_IN_USE:
+ return "ERR_STREAM_IN_USE";
+ case NGHTTP3_ERR_MALFORMED_HTTP_HEADER:
+ return "ERR_MALFORMED_HTTP_HEADER";
+ case NGHTTP3_ERR_REMOVE_HTTP_HEADER:
+ return "ERR_REMOVE_HTTP_HEADER";
+ case NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING:
+ return "ERR_MALFORMED_HTTP_MESSAGING";
+ case NGHTTP3_ERR_QPACK_FATAL:
+ return "ERR_QPACK_FATAL";
+ case NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE:
+ return "ERR_QPACK_HEADER_TOO_LARGE";
+ case NGHTTP3_ERR_STREAM_NOT_FOUND:
+ return "ERR_STREAM_NOT_FOUND";
+ case NGHTTP3_ERR_CONN_CLOSING:
+ return "ERR_CONN_CLOSING";
+ case NGHTTP3_ERR_STREAM_DATA_OVERFLOW:
+ return "ERR_STREAM_DATA_OVERFLOW";
+ case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED:
+ return "ERR_QPACK_DECOMPRESSION_FAILED";
+ case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR:
+ return "ERR_QPACK_ENCODER_STREAM_ERROR";
+ case NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR:
+ return "ERR_QPACK_DECODER_STREAM_ERROR";
+ case NGHTTP3_ERR_H3_FRAME_UNEXPECTED:
+ return "ERR_H3_FRAME_UNEXPECTED";
+ case NGHTTP3_ERR_H3_FRAME_ERROR:
+ return "ERR_H3_FRAME_ERROR";
+ case NGHTTP3_ERR_H3_MISSING_SETTINGS:
+ return "ERR_H3_MISSING_SETTINGS";
+ case NGHTTP3_ERR_H3_INTERNAL_ERROR:
+ return "ERR_H3_INTERNAL_ERROR";
+ case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM:
+ return "ERR_CLOSED_CRITICAL_STREAM";
+ case NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR:
+ return "ERR_H3_GENERAL_PROTOCOL_ERROR";
+ case NGHTTP3_ERR_H3_ID_ERROR:
+ return "ERR_H3_ID_ERROR";
+ case NGHTTP3_ERR_H3_SETTINGS_ERROR:
+ return "ERR_H3_SETTINGS_ERROR";
+ case NGHTTP3_ERR_H3_STREAM_CREATION_ERROR:
+ return "ERR_H3_STREAM_CREATION_ERROR";
+ case NGHTTP3_ERR_NOMEM:
+ return "ERR_NOMEM";
+ case NGHTTP3_ERR_CALLBACK_FAILURE:
+ return "ERR_CALLBACK_FAILURE";
+ default:
+ return "(unknown)";
+ }
+}
+
+uint64_t nghttp3_err_infer_quic_app_error_code(int liberr) {
+ switch (liberr) {
+ case 0:
+ return NGHTTP3_H3_NO_ERROR;
+ case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED:
+ return NGHTTP3_QPACK_DECOMPRESSION_FAILED;
+ case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR:
+ return NGHTTP3_QPACK_ENCODER_STREAM_ERROR;
+ case NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR:
+ return NGHTTP3_QPACK_DECODER_STREAM_ERROR;
+ case NGHTTP3_ERR_H3_FRAME_UNEXPECTED:
+ return NGHTTP3_H3_FRAME_UNEXPECTED;
+ case NGHTTP3_ERR_H3_FRAME_ERROR:
+ return NGHTTP3_H3_FRAME_ERROR;
+ case NGHTTP3_ERR_H3_MISSING_SETTINGS:
+ return NGHTTP3_H3_MISSING_SETTINGS;
+ case NGHTTP3_ERR_H3_INTERNAL_ERROR:
+ case NGHTTP3_ERR_NOMEM:
+ case NGHTTP3_ERR_CALLBACK_FAILURE:
+ return NGHTTP3_H3_INTERNAL_ERROR;
+ case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM:
+ return NGHTTP3_H3_CLOSED_CRITICAL_STREAM;
+ case NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR:
+ return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR;
+ case NGHTTP3_ERR_H3_ID_ERROR:
+ return NGHTTP3_H3_ID_ERROR;
+ case NGHTTP3_ERR_H3_SETTINGS_ERROR:
+ return NGHTTP3_H3_SETTINGS_ERROR;
+ case NGHTTP3_ERR_H3_STREAM_CREATION_ERROR:
+ return NGHTTP3_H3_STREAM_CREATION_ERROR;
+ case NGHTTP3_ERR_MALFORMED_HTTP_HEADER:
+ case NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING:
+ return NGHTTP3_H3_MESSAGE_ERROR;
+ default:
+ return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR;
+ }
+}
+
+int nghttp3_err_is_fatal(int liberr) { return liberr < NGHTTP3_ERR_FATAL; }
diff --git a/lib/nghttp3_err.h b/lib/nghttp3_err.h
new file mode 100644
index 0000000..2fa914f
--- /dev/null
+++ b/lib/nghttp3_err.h
@@ -0,0 +1,34 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_ERR_H
+#define NGHTTP3_ERR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#endif /* NGHTTP3_ERR_H */
diff --git a/lib/nghttp3_frame.c b/lib/nghttp3_frame.c
new file mode 100644
index 0000000..e7d64fb
--- /dev/null
+++ b/lib/nghttp3_frame.c
@@ -0,0 +1,204 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_frame.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "nghttp3_conv.h"
+#include "nghttp3_str.h"
+
+uint8_t *nghttp3_frame_write_hd(uint8_t *p, const nghttp3_frame_hd *hd) {
+ p = nghttp3_put_varint(p, hd->type);
+ p = nghttp3_put_varint(p, hd->length);
+ return p;
+}
+
+size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd) {
+ return nghttp3_put_varintlen(hd->type) + nghttp3_put_varintlen(hd->length);
+}
+
+uint8_t *nghttp3_frame_write_settings(uint8_t *p,
+ const nghttp3_frame_settings *fr) {
+ size_t i;
+
+ p = nghttp3_frame_write_hd(p, &fr->hd);
+
+ for (i = 0; i < fr->niv; ++i) {
+ p = nghttp3_put_varint(p, (int64_t)fr->iv[i].id);
+ p = nghttp3_put_varint(p, (int64_t)fr->iv[i].value);
+ }
+
+ return p;
+}
+
+size_t nghttp3_frame_write_settings_len(int64_t *ppayloadlen,
+ const nghttp3_frame_settings *fr) {
+ size_t payloadlen = 0;
+ size_t i;
+
+ for (i = 0; i < fr->niv; ++i) {
+ payloadlen += nghttp3_put_varintlen((int64_t)fr->iv[i].id) +
+ nghttp3_put_varintlen((int64_t)fr->iv[i].value);
+ }
+
+ *ppayloadlen = (int64_t)payloadlen;
+
+ return nghttp3_put_varintlen(NGHTTP3_FRAME_SETTINGS) +
+ nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen;
+}
+
+uint8_t *nghttp3_frame_write_goaway(uint8_t *p,
+ const nghttp3_frame_goaway *fr) {
+ p = nghttp3_frame_write_hd(p, &fr->hd);
+ p = nghttp3_put_varint(p, fr->id);
+
+ return p;
+}
+
+size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen,
+ const nghttp3_frame_goaway *fr) {
+ size_t payloadlen = nghttp3_put_varintlen(fr->id);
+
+ *ppayloadlen = (int64_t)payloadlen;
+
+ return nghttp3_put_varintlen(NGHTTP3_FRAME_GOAWAY) +
+ nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen;
+}
+
+uint8_t *
+nghttp3_frame_write_priority_update(uint8_t *p,
+ const nghttp3_frame_priority_update *fr) {
+ p = nghttp3_frame_write_hd(p, &fr->hd);
+ p = nghttp3_put_varint(p, fr->pri_elem_id);
+
+ assert(fr->pri.urgency <= NGHTTP3_URGENCY_LOW);
+
+ *p++ = 'u';
+ *p++ = '=';
+ *p++ = (uint8_t)('0' + fr->pri.urgency);
+
+ if (fr->pri.inc) {
+#define NGHTTP3_PRIORITY_INCREMENTAL ", i"
+ p = nghttp3_cpymem(p, (const uint8_t *)NGHTTP3_PRIORITY_INCREMENTAL,
+ sizeof(NGHTTP3_PRIORITY_INCREMENTAL) - 1);
+ }
+
+ return p;
+}
+
+size_t nghttp3_frame_write_priority_update_len(
+ int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr) {
+ size_t payloadlen = nghttp3_put_varintlen(fr->pri_elem_id) + sizeof("u=U") -
+ 1 + (fr->pri.inc ? sizeof(", i") - 1 : 0);
+
+ *ppayloadlen = (int64_t)payloadlen;
+
+ return nghttp3_put_varintlen(fr->hd.type) +
+ nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen;
+}
+
+int nghttp3_nva_copy(nghttp3_nv **pnva, const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_mem *mem) {
+ size_t i;
+ uint8_t *data = NULL;
+ size_t buflen = 0;
+ nghttp3_nv *p;
+
+ if (nvlen == 0) {
+ *pnva = NULL;
+
+ return 0;
+ }
+
+ for (i = 0; i < nvlen; ++i) {
+ /* + 1 for null-termination */
+ if ((nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_NAME) == 0) {
+ buflen += nva[i].namelen + 1;
+ }
+ if ((nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_VALUE) == 0) {
+ buflen += nva[i].valuelen + 1;
+ }
+ }
+
+ buflen += sizeof(nghttp3_nv) * nvlen;
+
+ *pnva = nghttp3_mem_malloc(mem, buflen);
+
+ if (*pnva == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ p = *pnva;
+ data = (uint8_t *)(*pnva) + sizeof(nghttp3_nv) * nvlen;
+
+ for (i = 0; i < nvlen; ++i) {
+ p->flags = nva[i].flags;
+
+ if (nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_NAME) {
+ p->name = nva[i].name;
+ p->namelen = nva[i].namelen;
+ } else {
+ if (nva[i].namelen) {
+ memcpy(data, nva[i].name, nva[i].namelen);
+ }
+ p->name = data;
+ p->namelen = nva[i].namelen;
+ data[p->namelen] = '\0';
+ nghttp3_downcase(p->name, p->namelen);
+ data += nva[i].namelen + 1;
+ }
+
+ if (nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_VALUE) {
+ p->value = nva[i].value;
+ p->valuelen = nva[i].valuelen;
+ } else {
+ if (nva[i].valuelen) {
+ memcpy(data, nva[i].value, nva[i].valuelen);
+ }
+ p->value = data;
+ p->valuelen = nva[i].valuelen;
+ data[p->valuelen] = '\0';
+ data += nva[i].valuelen + 1;
+ }
+
+ ++p;
+ }
+ return 0;
+}
+
+void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem) {
+ nghttp3_mem_free(mem, nva);
+}
+
+void nghttp3_frame_headers_free(nghttp3_frame_headers *fr,
+ const nghttp3_mem *mem) {
+ if (fr == NULL) {
+ return;
+ }
+
+ nghttp3_nva_del(fr->nva, mem);
+}
diff --git a/lib/nghttp3_frame.h b/lib/nghttp3_frame.h
new file mode 100644
index 0000000..4ee161d
--- /dev/null
+++ b/lib/nghttp3_frame.h
@@ -0,0 +1,214 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_FRAME_H
+#define NGHTTP3_FRAME_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_buf.h"
+
+typedef enum nghttp3_frame_type {
+ NGHTTP3_FRAME_DATA = 0x00,
+ NGHTTP3_FRAME_HEADERS = 0x01,
+ NGHTTP3_FRAME_CANCEL_PUSH = 0x03,
+ NGHTTP3_FRAME_SETTINGS = 0x04,
+ NGHTTP3_FRAME_PUSH_PROMISE = 0x05,
+ NGHTTP3_FRAME_GOAWAY = 0x07,
+ NGHTTP3_FRAME_MAX_PUSH_ID = 0x0d,
+ /* PRIORITY_UPDATE: https://datatracker.ietf.org/doc/html/rfc9218 */
+ NGHTTP3_FRAME_PRIORITY_UPDATE = 0x0f0700,
+ NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID = 0x0f0701,
+} nghttp3_frame_type;
+
+typedef enum nghttp3_h2_reserved_type {
+ NGHTTP3_H2_FRAME_PRIORITY = 0x02,
+ NGHTTP3_H2_FRAME_PING = 0x06,
+ NGHTTP3_H2_FRAME_WINDOW_UPDATE = 0x08,
+ NGHTTP3_H2_FRAME_CONTINUATION = 0x9,
+} nghttp3_h2_reserved_type;
+
+typedef struct nghttp3_frame_hd {
+ int64_t type;
+ int64_t length;
+} nghttp3_frame_hd;
+
+typedef struct nghttp3_frame_data {
+ nghttp3_frame_hd hd;
+} nghttp3_frame_data;
+
+typedef struct nghttp3_frame_headers {
+ nghttp3_frame_hd hd;
+ nghttp3_nv *nva;
+ size_t nvlen;
+} nghttp3_frame_headers;
+
+#define NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE 0x06
+#define NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY 0x01
+#define NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS 0x07
+#define NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL 0x08
+
+#define NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH 0x2
+#define NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS 0x3
+#define NGHTTP3_H2_SETTINGS_ID_INITIAL_WINDOW_SIZE 0x4
+#define NGHTTP3_H2_SETTINGS_ID_MAX_FRAME_SIZE 0x5
+
+typedef struct nghttp3_settings_entry {
+ uint64_t id;
+ uint64_t value;
+} nghttp3_settings_entry;
+
+typedef struct nghttp3_frame_settings {
+ nghttp3_frame_hd hd;
+ size_t niv;
+ nghttp3_settings_entry iv[1];
+} nghttp3_frame_settings;
+
+typedef struct nghttp3_frame_goaway {
+ nghttp3_frame_hd hd;
+ int64_t id;
+} nghttp3_frame_goaway;
+
+typedef struct nghttp3_frame_priority_update {
+ nghttp3_frame_hd hd;
+ /* pri_elem_id is stream ID if hd.type ==
+ NGHTTP3_FRAME_PRIORITY_UPDATE. It is push ID if hd.type ==
+ NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID. It is undefined
+ otherwise. */
+ int64_t pri_elem_id;
+ nghttp3_pri pri;
+} nghttp3_frame_priority_update;
+
+typedef union nghttp3_frame {
+ nghttp3_frame_hd hd;
+ nghttp3_frame_data data;
+ nghttp3_frame_headers headers;
+ nghttp3_frame_settings settings;
+ nghttp3_frame_goaway goaway;
+ nghttp3_frame_priority_update priority_update;
+} nghttp3_frame;
+
+/*
+ * nghttp3_frame_write_hd writes frame header |hd| to |dest|. This
+ * function assumes that |dest| has enough space to write |hd|.
+ *
+ * This function returns |dest| plus the number of bytes written.
+ */
+uint8_t *nghttp3_frame_write_hd(uint8_t *dest, const nghttp3_frame_hd *hd);
+
+/*
+ * nghttp3_frame_write_hd_len returns the number of bytes required to
+ * write |hd|. hd->length must be set.
+ */
+size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd);
+
+/*
+ * nghttp3_frame_write_settings writes SETTINGS frame |fr| to |dest|.
+ * This function assumes that |dest| has enough space to write |fr|.
+ *
+ * This function returns |dest| plus the number of bytes written.
+ */
+uint8_t *nghttp3_frame_write_settings(uint8_t *dest,
+ const nghttp3_frame_settings *fr);
+
+/*
+ * nghttp3_frame_write_settings_len returns the number of bytes
+ * required to write |fr|. fr->hd.length is ignored. This function
+ * stores payload length in |*ppayloadlen|.
+ */
+size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen,
+ const nghttp3_frame_settings *fr);
+
+/*
+ * nghttp3_frame_write_goaway writes GOAWAY frame |fr| to |dest|.
+ * This function assumes that |dest| has enough space to write |fr|.
+ *
+ * This function returns |dest| plus the number of bytes written.
+ */
+uint8_t *nghttp3_frame_write_goaway(uint8_t *dest,
+ const nghttp3_frame_goaway *fr);
+
+/*
+ * nghttp3_frame_write_goaway_len returns the number of bytes required
+ * to write |fr|. fr->hd.length is ignored. This function stores
+ * payload length in |*ppayloadlen|.
+ */
+size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen,
+ const nghttp3_frame_goaway *fr);
+
+/*
+ * nghttp3_frame_write_priority_update writes PRIORITY_UPDATE frame
+ * |fr| to |dest|. This function assumes that |dest| has enough space
+ * to write |fr|.
+ *
+ * This function returns |dest| plus the number of bytes written;
+ */
+uint8_t *
+nghttp3_frame_write_priority_update(uint8_t *dest,
+ const nghttp3_frame_priority_update *fr);
+
+/*
+ * nghttp3_frame_write_priority_update_len returns the number of bytes
+ * required to write |fr|. fr->hd.length is ignored. This function
+ * stores payload length in |*ppayloadlen|.
+ */
+size_t nghttp3_frame_write_priority_update_len(
+ int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr);
+
+/*
+ * nghttp3_nva_copy copies name/value pairs from |nva|, which contains
+ * |nvlen| pairs, to |*nva_ptr|, which is dynamically allocated so
+ * that all items can be stored. The resultant name and value in
+ * nghttp2_nv are guaranteed to be NULL-terminated even if the input
+ * is not null-terminated.
+ *
+ * The |*pnva| must be freed using nghttp3_nva_del().
+ *
+ * This function returns 0 if it succeeds or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_nva_copy(nghttp3_nv **pnva, const nghttp3_nv *nva, size_t nvlen,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_nva_del frees |nva|.
+ */
+void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem);
+
+/*
+ * nghttp3_frame_headers_free frees memory allocated for |fr|. It
+ * assumes that fr->nva is created by nghttp3_nva_copy() or NULL.
+ */
+void nghttp3_frame_headers_free(nghttp3_frame_headers *fr,
+ const nghttp3_mem *mem);
+
+#endif /* NGHTTP3_FRAME_H */
diff --git a/lib/nghttp3_gaptr.c b/lib/nghttp3_gaptr.c
new file mode 100644
index 0000000..88cb49a
--- /dev/null
+++ b/lib/nghttp3_gaptr.c
@@ -0,0 +1,169 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_gaptr.h"
+
+#include <string.h>
+#include <assert.h>
+
+void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) {
+ nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, sizeof(nghttp3_range),
+ mem);
+
+ gaptr->mem = mem;
+}
+
+static int gaptr_gap_init(nghttp3_gaptr *gaptr) {
+ nghttp3_range range = {0, UINT64_MAX};
+ int rv;
+
+ rv = nghttp3_ksl_insert(&gaptr->gap, NULL, &range, NULL);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return 0;
+}
+
+void nghttp3_gaptr_free(nghttp3_gaptr *gaptr) {
+ if (gaptr == NULL) {
+ return;
+ }
+
+ nghttp3_ksl_free(&gaptr->gap);
+}
+
+int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset,
+ uint64_t datalen) {
+ int rv;
+ nghttp3_range k, m, l, r, q = {offset, offset + datalen};
+ nghttp3_ksl_it it;
+
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ rv = gaptr_gap_init(gaptr);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
+ nghttp3_ksl_range_exclusive_compar);
+
+ for (; !nghttp3_ksl_it_end(&it);) {
+ k = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+ m = nghttp3_range_intersect(&q, &k);
+ if (!nghttp3_range_len(&m)) {
+ break;
+ }
+
+ if (nghttp3_range_eq(&k, &m)) {
+ nghttp3_ksl_remove_hint(&gaptr->gap, &it, &it, &k);
+ continue;
+ }
+ nghttp3_range_cut(&l, &r, &k, &m);
+ if (nghttp3_range_len(&l)) {
+ nghttp3_ksl_update_key(&gaptr->gap, &k, &l);
+
+ if (nghttp3_range_len(&r)) {
+ rv = nghttp3_ksl_insert(&gaptr->gap, &it, &r, NULL);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ } else if (nghttp3_range_len(&r)) {
+ nghttp3_ksl_update_key(&gaptr->gap, &k, &r);
+ }
+ nghttp3_ksl_it_next(&it);
+ }
+ return 0;
+}
+
+uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr) {
+ nghttp3_ksl_it it;
+ nghttp3_range r;
+
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ return 0;
+ }
+
+ it = nghttp3_ksl_begin(&gaptr->gap);
+ r = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+
+ return r.begin;
+}
+
+nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
+ uint64_t offset) {
+ nghttp3_range q = {offset, offset + 1};
+ nghttp3_ksl_it it;
+
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ nghttp3_range r = {0, UINT64_MAX};
+ return r;
+ }
+
+ it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
+ nghttp3_ksl_range_exclusive_compar);
+
+ assert(!nghttp3_ksl_it_end(&it));
+
+ return *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+}
+
+int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset,
+ uint64_t datalen) {
+ nghttp3_range q = {offset, offset + datalen};
+ nghttp3_ksl_it it;
+ nghttp3_range k;
+ nghttp3_range m;
+
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ return 0;
+ }
+
+ it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
+ nghttp3_ksl_range_exclusive_compar);
+ k = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+ m = nghttp3_range_intersect(&q, &k);
+
+ return nghttp3_range_len(&m) == 0;
+}
+
+void nghttp3_gaptr_drop_first_gap(nghttp3_gaptr *gaptr) {
+ nghttp3_ksl_it it;
+ nghttp3_range r;
+
+ if (nghttp3_ksl_len(&gaptr->gap) == 0) {
+ return;
+ }
+
+ it = nghttp3_ksl_begin(&gaptr->gap);
+
+ assert(!nghttp3_ksl_it_end(&it));
+
+ r = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
+
+ nghttp3_ksl_remove_hint(&gaptr->gap, NULL, &it, &r);
+}
diff --git a/lib/nghttp3_gaptr.h b/lib/nghttp3_gaptr.h
new file mode 100644
index 0000000..7c83c84
--- /dev/null
+++ b/lib/nghttp3_gaptr.h
@@ -0,0 +1,99 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_GAPTR_H
+#define NGHTTP3_GAPTR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+#include "nghttp3_ksl.h"
+#include "nghttp3_range.h"
+
+/*
+ * nghttp3_gaptr maintains the gap in the range [0, UINT64_MAX).
+ */
+typedef struct nghttp3_gaptr {
+ /* gap maintains the range of offset which is not received
+ yet. Initially, its range is [0, UINT64_MAX). */
+ nghttp3_ksl gap;
+ /* mem is custom memory allocator */
+ const nghttp3_mem *mem;
+} nghttp3_gaptr;
+
+/*
+ * nghttp3_gaptr_init initializes |gaptr|.
+ */
+void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem);
+
+/*
+ * nghttp3_gaptr_free frees resources allocated for |gaptr|.
+ */
+void nghttp3_gaptr_free(nghttp3_gaptr *gaptr);
+
+/*
+ * nghttp3_gaptr_push adds new data of length |datalen| at the stream
+ * offset |offset|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory
+ */
+int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, uint64_t datalen);
+
+/*
+ * nghttp3_gaptr_first_gap_offset returns the offset to the first gap.
+ * If there is no gap, it returns UINT64_MAX.
+ */
+uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr);
+
+/*
+ * nghttp3_gaptr_get_first_gap_after returns the first gap which
+ * overlaps or comes after |offset|.
+ */
+nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
+ uint64_t offset);
+
+/*
+ * nghttp3_gaptr_is_pushed returns nonzero if range [offset, offset +
+ * datalen) is completely pushed into this object.
+ */
+int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset,
+ uint64_t datalen);
+
+/*
+ * nghttp3_gaptr_drop_first_gap deletes the first gap entirely as if
+ * the range is pushed. This function assumes that at least one gap
+ * exists.
+ */
+void nghttp3_gaptr_drop_first_gap(nghttp3_gaptr *gaptr);
+
+#endif /* NGHTTP3_GAPTR_H */
diff --git a/lib/nghttp3_http.c b/lib/nghttp3_http.c
new file mode 100644
index 0000000..29af976
--- /dev/null
+++ b/lib/nghttp3_http.c
@@ -0,0 +1,1654 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2015 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_http.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "nghttp3_stream.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_conv.h"
+#include "nghttp3_unreachable.h"
+
+static uint8_t downcase(uint8_t c) {
+ return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c;
+}
+
+/*
+ * memieq returns 1 if the data pointed by |a| of length |n| equals to
+ * |b| of the same length in case-insensitive manner. The data
+ * pointed by |a| must not include upper cased letters (A-Z).
+ */
+static int memieq(const void *a, const void *b, size_t n) {
+ size_t i;
+ const uint8_t *aa = a, *bb = b;
+
+ for (i = 0; i < n; ++i) {
+ if (aa[i] != downcase(bb[i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
+
+static int64_t parse_uint(const uint8_t *s, size_t len) {
+ int64_t n = 0;
+ size_t i;
+ if (len == 0) {
+ return -1;
+ }
+ for (i = 0; i < len; ++i) {
+ if ('0' <= s[i] && s[i] <= '9') {
+ if (n > INT64_MAX / 10) {
+ return -1;
+ }
+ n *= 10;
+ if (n > INT64_MAX - (s[i] - '0')) {
+ return -1;
+ }
+ n += s[i] - '0';
+ continue;
+ }
+ return -1;
+ }
+ return n;
+}
+
+static int check_pseudo_header(nghttp3_http_state *http,
+ const nghttp3_qpack_nv *nv, uint32_t flag) {
+ if ((http->flags & flag) || nv->value->len == 0) {
+ return 0;
+ }
+ http->flags |= flag;
+ return 1;
+}
+
+static int expect_response_body(nghttp3_http_state *http) {
+ return (http->flags & NGHTTP3_HTTP_FLAG_METH_HEAD) == 0 &&
+ http->status_code / 100 != 1 && http->status_code != 304 &&
+ http->status_code != 204;
+}
+
+/* For "http" or "https" URIs, OPTIONS request may have "*" in :path
+ header field to represent system-wide OPTIONS request. Otherwise,
+ :path header field value must start with "/". This function must
+ be called after ":method" header field was received. This function
+ returns nonzero if path is valid.*/
+static int check_path_flags(nghttp3_http_state *http) {
+ return (http->flags & NGHTTP3_HTTP_FLAG_SCHEME_HTTP) == 0 ||
+ ((http->flags & NGHTTP3_HTTP_FLAG_PATH_REGULAR) ||
+ ((http->flags & NGHTTP3_HTTP_FLAG_METH_OPTIONS) &&
+ (http->flags & NGHTTP3_HTTP_FLAG_PATH_ASTERISK)));
+}
+
+/* Generated by genchartbl.py */
+static const int SF_KEY_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
+ 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
+ 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
+ 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
+ 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
+ 0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */,
+ 0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */,
+ 0 /* ( */, 0 /* ) */, 1 /* * */, 0 /* + */, 0 /* , */,
+ 1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */,
+ 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
+ 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
+ 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
+ 0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */,
+ 0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */,
+ 0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */,
+ 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */,
+ 0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */,
+ 0 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */,
+ 1 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
+ 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
+ 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
+ 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */,
+ 0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
+ 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
+ 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
+ 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
+ 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
+ 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
+ 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
+ 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
+ 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
+ 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
+ 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
+ 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
+ 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
+ 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
+ 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
+ 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
+ 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
+ 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
+ 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
+ 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
+ 0 /* 0xff */,
+};
+
+static nghttp3_ssize sf_parse_key(const uint8_t *begin, const uint8_t *end) {
+ const uint8_t *p = begin;
+
+ if ((*p < 'a' || 'z' < *p) && *p != '*') {
+ return -1;
+ }
+
+ for (; p != end && SF_KEY_CHARS[*p]; ++p)
+ ;
+
+ return p - begin;
+}
+
+static nghttp3_ssize sf_parse_integer_or_decimal(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+ int sign = 1;
+ int64_t value = 0;
+ int type = NGHTTP3_SF_VALUE_TYPE_INTEGER;
+ size_t len = 0;
+ size_t fpos = 0;
+ size_t i;
+
+ if (*p == '-') {
+ if (++p == end) {
+ return -1;
+ }
+
+ sign = -1;
+ }
+
+ if (*p < '0' || '9' < *p) {
+ return -1;
+ }
+
+ for (; p != end; ++p) {
+ switch (*p) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ value *= 10;
+ value += *p - '0';
+
+ if (++len > 15) {
+ return -1;
+ }
+
+ break;
+ case '.':
+ if (type != NGHTTP3_SF_VALUE_TYPE_INTEGER) {
+ goto fin;
+ }
+
+ if (len > 12) {
+ return -1;
+ }
+ fpos = len;
+ type = NGHTTP3_SF_VALUE_TYPE_DECIMAL;
+
+ break;
+ default:
+ goto fin;
+ };
+ }
+
+fin:
+ switch (type) {
+ case NGHTTP3_SF_VALUE_TYPE_INTEGER:
+ if (dest) {
+ dest->type = (uint8_t)type;
+ dest->i = value * sign;
+ }
+
+ return p - begin;
+ case NGHTTP3_SF_VALUE_TYPE_DECIMAL:
+ if (fpos == len || len - fpos > 3) {
+ return -1;
+ }
+
+ if (dest) {
+ dest->type = (uint8_t)type;
+ dest->d = (double)value;
+ for (i = len - fpos; i > 0; --i) {
+ dest->d /= (double)10;
+ }
+ dest->d *= sign;
+ }
+
+ return p - begin;
+ default:
+ nghttp3_unreachable();
+ }
+}
+
+/* Generated by genchartbl.py */
+static const int SF_DQUOTE_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
+ 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
+ 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
+ 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
+ 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
+ 0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 0 /* " */,
+ 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */,
+ 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
+ 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
+ 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
+ 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */,
+ 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
+ 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
+ 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
+ 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
+ 1 /* Z */, 1 /* [ */, 0 /* \ */, 1 /* ] */, 1 /* ^ */,
+ 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
+ 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
+ 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
+ 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */,
+ 1 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
+ 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
+ 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
+ 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
+ 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
+ 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
+ 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
+ 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
+ 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
+ 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
+ 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
+ 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
+ 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
+ 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
+ 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
+ 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
+ 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
+ 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
+ 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
+ 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
+ 0 /* 0xff */,
+};
+
+static nghttp3_ssize sf_parse_string(nghttp3_sf_value *dest,
+ const uint8_t *begin, const uint8_t *end) {
+ const uint8_t *p = begin;
+
+ if (*p++ != '"') {
+ return -1;
+ }
+
+ for (; p != end; ++p) {
+ switch (*p) {
+ case '\\':
+ if (++p == end) {
+ return -1;
+ }
+
+ switch (*p) {
+ case '"':
+ case '\\':
+ break;
+ default:
+ return -1;
+ }
+
+ break;
+ case '"':
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_STRING;
+ dest->s.base = begin + 1;
+ dest->s.len = (size_t)(p - dest->s.base);
+ }
+
+ ++p;
+
+ return p - begin;
+ default:
+ if (!SF_DQUOTE_CHARS[*p]) {
+ return -1;
+ }
+ }
+ }
+
+ return -1;
+}
+
+/* Generated by genchartbl.py */
+static const int SF_TOKEN_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
+ 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
+ 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
+ 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
+ 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
+ 0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */,
+ 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */,
+ 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
+ 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
+ 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 0 /* ; */,
+ 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
+ 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
+ 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
+ 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
+ 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
+ 1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */,
+ 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
+ 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
+ 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
+ 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */,
+ 0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
+ 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
+ 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
+ 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
+ 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
+ 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
+ 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
+ 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
+ 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
+ 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
+ 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
+ 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
+ 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
+ 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
+ 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
+ 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
+ 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
+ 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
+ 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
+ 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
+ 0 /* 0xff */,
+};
+
+static nghttp3_ssize sf_parse_token(nghttp3_sf_value *dest,
+ const uint8_t *begin, const uint8_t *end) {
+ const uint8_t *p = begin;
+
+ if ((*p < 'A' || 'Z' < *p) && (*p < 'a' || 'z' < *p) && *p != '*') {
+ return -1;
+ }
+
+ for (; p != end && SF_TOKEN_CHARS[*p]; ++p)
+ ;
+
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_TOKEN;
+ dest->s.base = begin;
+ dest->s.len = (size_t)(p - begin);
+ }
+
+ return p - begin;
+}
+
+/* Generated by genchartbl.py */
+static const int SF_BYTESEQ_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */,
+ 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */,
+ 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */,
+ 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */,
+ 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */,
+ 0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */,
+ 0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */,
+ 0 /* ( */, 0 /* ) */, 0 /* * */, 1 /* + */, 0 /* , */,
+ 0 /* - */, 0 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */,
+ 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */,
+ 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
+ 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */,
+ 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */,
+ 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */,
+ 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */,
+ 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */,
+ 1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */,
+ 0 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */,
+ 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */,
+ 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */,
+ 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */,
+ 0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */,
+ 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */,
+ 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */,
+ 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */,
+ 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */,
+ 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */,
+ 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */,
+ 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */,
+ 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */,
+ 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */,
+ 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */,
+ 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */,
+ 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */,
+ 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */,
+ 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */,
+ 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */,
+ 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */,
+ 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */,
+ 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */,
+ 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */,
+ 0 /* 0xff */,
+};
+
+static nghttp3_ssize sf_parse_byteseq(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+
+ if (*p++ != ':') {
+ return -1;
+ }
+
+ for (; p != end; ++p) {
+ switch (*p) {
+ case ':':
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_BYTESEQ;
+ dest->s.base = begin + 1;
+ dest->s.len = (size_t)(p - dest->s.base);
+ }
+
+ ++p;
+
+ return p - begin;
+ default:
+ if (!SF_BYTESEQ_CHARS[*p]) {
+ return -1;
+ }
+ }
+ }
+
+ return -1;
+}
+
+static nghttp3_ssize sf_parse_boolean(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+ int b;
+
+ if (*p++ != '?') {
+ return -1;
+ }
+
+ if (p == end) {
+ return -1;
+ }
+
+ switch (*p++) {
+ case '0':
+ b = 0;
+ break;
+ case '1':
+ b = 1;
+ break;
+ default:
+ return -1;
+ }
+
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_BOOLEAN;
+ dest->b = b;
+ }
+
+ return p - begin;
+}
+
+static nghttp3_ssize sf_parse_bare_item(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ switch (*begin) {
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return sf_parse_integer_or_decimal(dest, begin, end);
+ case '"':
+ return sf_parse_string(dest, begin, end);
+ case '*':
+ return sf_parse_token(dest, begin, end);
+ case ':':
+ return sf_parse_byteseq(dest, begin, end);
+ case '?':
+ return sf_parse_boolean(dest, begin, end);
+ default:
+ if (('A' <= *begin && *begin <= 'Z') || ('a' <= *begin && *begin <= 'z')) {
+ return sf_parse_token(dest, begin, end);
+ }
+ return -1;
+ }
+}
+
+#define sf_discard_sp_end_err(BEGIN, END, ERR) \
+ for (;; ++(BEGIN)) { \
+ if ((BEGIN) == (END)) { \
+ return (ERR); \
+ } \
+ if (*(BEGIN) != ' ') { \
+ break; \
+ } \
+ }
+
+static nghttp3_ssize sf_parse_params(const uint8_t *begin, const uint8_t *end) {
+ const uint8_t *p = begin;
+ nghttp3_ssize slen;
+
+ for (; p != end && *p == ';';) {
+ ++p;
+
+ sf_discard_sp_end_err(p, end, -1);
+
+ slen = sf_parse_key(p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+
+ if (p == end || *p != '=') {
+ /* Boolean true */
+ } else if (++p == end) {
+ return -1;
+ } else {
+ slen = sf_parse_bare_item(NULL, p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+ }
+ }
+
+ return p - begin;
+}
+
+static nghttp3_ssize sf_parse_item(nghttp3_sf_value *dest, const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+ nghttp3_ssize slen;
+
+ slen = sf_parse_bare_item(dest, p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+
+ slen = sf_parse_params(p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+
+ return p - begin;
+}
+
+nghttp3_ssize nghttp3_sf_parse_item(nghttp3_sf_value *dest,
+ const uint8_t *begin, const uint8_t *end) {
+ return sf_parse_item(dest, begin, end);
+}
+
+static nghttp3_ssize sf_parse_inner_list(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ const uint8_t *p = begin;
+ nghttp3_ssize slen;
+
+ if (*p++ != '(') {
+ return -1;
+ }
+
+ for (;;) {
+ sf_discard_sp_end_err(p, end, -1);
+
+ if (*p == ')') {
+ ++p;
+
+ slen = sf_parse_params(p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+
+ if (dest) {
+ dest->type = NGHTTP3_SF_VALUE_TYPE_INNER_LIST;
+ }
+
+ return p - begin;
+ }
+
+ slen = sf_parse_item(NULL, p, end);
+ if (slen < 0) {
+ return -1;
+ }
+
+ p += slen;
+
+ if (p == end || (*p != ' ' && *p != ')')) {
+ return -1;
+ }
+ }
+}
+
+nghttp3_ssize nghttp3_sf_parse_inner_list(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ return sf_parse_inner_list(dest, begin, end);
+}
+
+static nghttp3_ssize sf_parse_item_or_inner_list(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ if (*begin == '(') {
+ return sf_parse_inner_list(dest, begin, end);
+ }
+
+ return sf_parse_item(dest, begin, end);
+}
+
+#define sf_discard_ows(BEGIN, END) \
+ for (;; ++(BEGIN)) { \
+ if ((BEGIN) == (END)) { \
+ goto fin; \
+ } \
+ if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \
+ break; \
+ } \
+ }
+
+#define sf_discard_ows_end_err(BEGIN, END, ERR) \
+ for (;; ++(BEGIN)) { \
+ if ((BEGIN) == (END)) { \
+ return (ERR); \
+ } \
+ if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \
+ break; \
+ } \
+ }
+
+int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value,
+ size_t valuelen) {
+ const uint8_t *p = value, *end = value + valuelen;
+ nghttp3_ssize slen;
+ nghttp3_sf_value val;
+ nghttp3_pri pri = *dest;
+ const uint8_t *key;
+ size_t keylen;
+
+ for (; p != end && *p == ' '; ++p)
+ ;
+
+ for (; p != end;) {
+ slen = sf_parse_key(p, end);
+ if (slen < 0) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ key = p;
+ keylen = (size_t)slen;
+
+ p += slen;
+
+ if (p == end || *p != '=') {
+ /* Boolean true */
+ val.type = NGHTTP3_SF_VALUE_TYPE_BOOLEAN;
+ val.b = 1;
+
+ slen = sf_parse_params(p, end);
+ if (slen < 0) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+ } else if (++p == end) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ } else {
+ slen = sf_parse_item_or_inner_list(&val, p, end);
+ if (slen < 0) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+ }
+
+ p += slen;
+
+ if (keylen == 1) {
+ switch (key[0]) {
+ case 'i':
+ if (val.type != NGHTTP3_SF_VALUE_TYPE_BOOLEAN) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ pri.inc = val.b;
+
+ break;
+ case 'u':
+ if (val.type != NGHTTP3_SF_VALUE_TYPE_INTEGER ||
+ val.i < NGHTTP3_URGENCY_HIGH || NGHTTP3_URGENCY_LOW < val.i) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ pri.urgency = (uint32_t)val.i;
+
+ break;
+ }
+ }
+
+ sf_discard_ows(p, end);
+
+ if (*p++ != ',') {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ sf_discard_ows_end_err(p, end, NGHTTP3_ERR_INVALID_ARGUMENT);
+ }
+
+fin:
+ *dest = pri;
+
+ return 0;
+}
+
+static int http_request_on_header(nghttp3_http_state *http,
+ nghttp3_qpack_nv *nv, int trailers,
+ int connect_protocol) {
+ nghttp3_pri pri;
+
+ if (nv->name->base[0] == ':') {
+ if (trailers ||
+ (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ switch (nv->token) {
+ case NGHTTP3_QPACK_TOKEN__AUTHORITY:
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__AUTHORITY)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__METHOD:
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__METHOD)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ switch (nv->value->len) {
+ case 4:
+ if (lstreq("HEAD", nv->value->base, nv->value->len)) {
+ http->flags |= NGHTTP3_HTTP_FLAG_METH_HEAD;
+ }
+ break;
+ case 7:
+ switch (nv->value->base[6]) {
+ case 'T':
+ if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
+ http->flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT;
+ }
+ break;
+ case 'S':
+ if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
+ http->flags |= NGHTTP3_HTTP_FLAG_METH_OPTIONS;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__PATH:
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PATH)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if (nv->value->base[0] == '/') {
+ http->flags |= NGHTTP3_HTTP_FLAG_PATH_REGULAR;
+ } else if (nv->value->len == 1 && nv->value->base[0] == '*') {
+ http->flags |= NGHTTP3_HTTP_FLAG_PATH_ASTERISK;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__SCHEME:
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__SCHEME)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ /* scheme is case-insensitive:
+ https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 */
+ if (lstrieq("http", nv->value->base, nv->value->len) ||
+ lstrieq("https", nv->value->base, nv->value->len)) {
+ http->flags |= NGHTTP3_HTTP_FLAG_SCHEME_HTTP;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__PROTOCOL:
+ if (!connect_protocol) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PROTOCOL)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN_HOST:
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG_HOST)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: {
+ /* https://tools.ietf.org/html/rfc7230#section-4.1.2: A sender
+ MUST NOT generate a trailer that contains a field necessary for
+ message framing (e.g., Transfer-Encoding and Content-Length),
+ ... */
+ if (trailers) {
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ if (http->content_length != -1) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->content_length = parse_uint(nv->value->base, nv->value->len);
+ if (http->content_length == -1) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ }
+ /* disallowed header fields */
+ case NGHTTP3_QPACK_TOKEN_CONNECTION:
+ case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE:
+ case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION:
+ case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING:
+ case NGHTTP3_QPACK_TOKEN_UPGRADE:
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ case NGHTTP3_QPACK_TOKEN_TE:
+ if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN_PRIORITY:
+ if (!trailers && !(http->flags & NGHTTP3_HTTP_FLAG_BAD_PRIORITY)) {
+ pri.urgency = nghttp3_pri_uint8_urgency(http->pri);
+ pri.inc = nghttp3_pri_uint8_inc(http->pri);
+ if (nghttp3_http_parse_priority(&pri, nv->value->base, nv->value->len) ==
+ 0) {
+ http->pri = nghttp3_pri_to_uint8(&pri);
+ http->flags |= NGHTTP3_HTTP_FLAG_PRIORITY;
+ } else {
+ http->flags &= ~NGHTTP3_HTTP_FLAG_PRIORITY;
+ http->flags |= NGHTTP3_HTTP_FLAG_BAD_PRIORITY;
+ }
+ }
+ break;
+ default:
+ if (nv->name->base[0] == ':') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ return 0;
+}
+
+static int http_response_on_header(nghttp3_http_state *http,
+ nghttp3_qpack_nv *nv, int trailers) {
+ if (nv->name->base[0] == ':') {
+ if (trailers ||
+ (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ switch (nv->token) {
+ case NGHTTP3_QPACK_TOKEN__STATUS: {
+ if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__STATUS)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if (nv->value->len != 3) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
+ if (http->status_code < 100 || http->status_code == 101) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ }
+ case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: {
+ /* https://tools.ietf.org/html/rfc7230#section-4.1.2: A sender
+ MUST NOT generate a trailer that contains a field necessary for
+ message framing (e.g., Transfer-Encoding and Content-Length),
+ ... */
+ if (trailers) {
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ if (http->status_code == 204) {
+ /* content-length header field in 204 response is prohibited by
+ RFC 7230. But some widely used servers send content-length:
+ 0. Until they get fixed, we ignore it. */
+ if (http->content_length != -1) {
+ /* Found multiple content-length field */
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if (!lstrieq("0", nv->value->base, nv->value->len)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->content_length = 0;
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ if (http->status_code / 100 == 1) {
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */
+ if (http->status_code / 100 == 2 &&
+ (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT)) {
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+ if (http->content_length != -1) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->content_length = parse_uint(nv->value->base, nv->value->len);
+ if (http->content_length == -1) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ }
+ /* disallowed header fields */
+ case NGHTTP3_QPACK_TOKEN_CONNECTION:
+ case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE:
+ case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION:
+ case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING:
+ case NGHTTP3_QPACK_TOKEN_UPGRADE:
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ case NGHTTP3_QPACK_TOKEN_TE:
+ if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ break;
+ default:
+ if (nv->name->base[0] == ':') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ return 0;
+}
+
+/* Generated by genauthroitychartbl.py */
+static char VALID_AUTHORITY_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
+ 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
+ 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
+ 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
+ 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
+ 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
+ 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
+ 0 /* SPC */, 1 /* ! */, 0 /* " */, 0 /* # */,
+ 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
+ 1 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
+ 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
+ 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
+ 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
+ 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */,
+ 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
+ 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
+ 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
+ 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
+ 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
+ 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */,
+ 0 /* \ */, 1 /* ] */, 0 /* ^ */, 1 /* _ */,
+ 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
+ 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
+ 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
+ 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
+ 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */,
+ 0 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */,
+ 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
+ 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
+ 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
+ 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
+ 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
+ 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
+ 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
+ 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
+ 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
+ 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
+ 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
+ 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
+ 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
+ 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
+ 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
+ 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
+ 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
+ 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
+ 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
+ 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
+ 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
+ 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
+ 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
+ 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
+ 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
+ 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
+};
+
+static int check_authority(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+ for (last = value + len; value != last; ++value) {
+ if (!VALID_AUTHORITY_CHARS[*value]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int check_scheme(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+ if (len == 0) {
+ return 0;
+ }
+
+ if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) {
+ return 0;
+ }
+
+ last = value + len;
+ ++value;
+
+ for (; value != last; ++value) {
+ if (!(('A' <= *value && *value <= 'Z') ||
+ ('a' <= *value && *value <= 'z') ||
+ ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' ||
+ *value == '.')) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Generated by genmethodchartbl.py */
+static char VALID_METHOD_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
+ 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
+ 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
+ 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
+ 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
+ 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
+ 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
+ 0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */,
+ 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */,
+ 0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
+ 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
+ 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
+ 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
+ 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */,
+ 0 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
+ 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
+ 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
+ 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
+ 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
+ 1 /* X */, 1 /* Y */, 1 /* Z */, 0 /* [ */,
+ 0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */,
+ 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
+ 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
+ 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
+ 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
+ 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */,
+ 1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */,
+ 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
+ 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
+ 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
+ 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
+ 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
+ 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
+ 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
+ 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
+ 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
+ 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
+ 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
+ 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
+ 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
+ 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
+ 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
+ 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
+ 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
+ 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
+ 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
+ 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
+ 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
+ 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
+ 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
+ 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
+ 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
+ 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
+};
+
+static int check_method(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+ if (len == 0) {
+ return 0;
+ }
+ for (last = value + len; value != last; ++value) {
+ if (!VALID_METHOD_CHARS[*value]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Generated by genpathchartbl.py */
+static char VALID_PATH_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
+ 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
+ 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
+ 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
+ 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
+ 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
+ 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
+ 0 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */,
+ 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
+ 1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */,
+ 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
+ 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
+ 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
+ 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */,
+ 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
+ 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
+ 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
+ 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
+ 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
+ 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */,
+ 1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */,
+ 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
+ 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
+ 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
+ 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
+ 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */,
+ 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */,
+ 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,
+ 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,
+ 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
+ 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,
+ 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,
+ 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,
+ 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,
+ 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
+ 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,
+ 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,
+ 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,
+ 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,
+ 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
+ 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,
+ 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,
+ 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,
+ 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,
+ 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
+ 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,
+ 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,
+ 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */,
+ 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */,
+ 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
+ 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */,
+ 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */,
+ 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */,
+ 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */,
+ 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
+ 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */,
+ 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */,
+ 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */,
+ 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */
+};
+
+static int check_path(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+ for (last = value + len; value != last; ++value) {
+ if (!VALID_PATH_CHARS[*value]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int nghttp3_http_on_header(nghttp3_http_state *http, nghttp3_qpack_nv *nv,
+ int request, int trailers, int connect_protocol) {
+ int rv;
+ size_t i;
+ uint8_t c;
+
+ if (!nghttp3_check_header_name(nv->name->base, nv->name->len)) {
+ if (nv->name->len > 0 && nv->name->base[0] == ':') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ /* header field name must be lower-cased without exception */
+ for (i = 0; i < nv->name->len; ++i) {
+ c = nv->name->base[i];
+ if ('A' <= c && c <= 'Z') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+ /* When ignoring regular header fields, we set this flag so that
+ we still enforce header field ordering rule for pseudo header
+ fields. */
+ http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+
+ assert(nv->name->len > 0);
+
+ switch (nv->token) {
+ case NGHTTP3_QPACK_TOKEN__METHOD:
+ rv = check_method(nv->value->base, nv->value->len);
+ break;
+ case NGHTTP3_QPACK_TOKEN__SCHEME:
+ rv = check_scheme(nv->value->base, nv->value->len);
+ break;
+ case NGHTTP3_QPACK_TOKEN__AUTHORITY:
+ case NGHTTP3_QPACK_TOKEN_HOST:
+ if (request) {
+ rv = check_authority(nv->value->base, nv->value->len);
+ } else {
+ /* The use of host field in response field section is
+ undefined. */
+ rv = nghttp3_check_header_value(nv->value->base, nv->value->len);
+ }
+ break;
+ case NGHTTP3_QPACK_TOKEN__PATH:
+ rv = check_path(nv->value->base, nv->value->len);
+ break;
+ default:
+ rv = nghttp3_check_header_value(nv->value->base, nv->value->len);
+ }
+
+ if (rv == 0) {
+ if (nv->name->base[0] == ':') {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ /* When ignoring regular header fields, we set this flag so that
+ we still enforce header field ordering rule for pseudo header
+ fields. */
+ http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
+ return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
+ }
+
+ if (request) {
+ rv = http_request_on_header(http, nv, trailers, connect_protocol);
+ } else {
+ rv = http_response_on_header(http, nv, trailers);
+ }
+
+ if (nv->name->base[0] != ':') {
+ switch (rv) {
+ case 0:
+ case NGHTTP3_ERR_REMOVE_HTTP_HEADER:
+ http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
+ break;
+ }
+ }
+
+ return rv;
+}
+
+int nghttp3_http_on_request_headers(nghttp3_http_state *http) {
+ if (!(http->flags & NGHTTP3_HTTP_FLAG__PROTOCOL) &&
+ (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT)) {
+ if ((http->flags & (NGHTTP3_HTTP_FLAG__SCHEME | NGHTTP3_HTTP_FLAG__PATH)) ||
+ (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ http->content_length = -1;
+ } else {
+ if ((http->flags & NGHTTP3_HTTP_FLAG_REQ_HEADERS) !=
+ NGHTTP3_HTTP_FLAG_REQ_HEADERS ||
+ (http->flags &
+ (NGHTTP3_HTTP_FLAG__AUTHORITY | NGHTTP3_HTTP_FLAG_HOST)) == 0) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if ((http->flags & NGHTTP3_HTTP_FLAG__PROTOCOL) &&
+ ((http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) == 0 ||
+ (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ if (!check_path_flags(http)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+ }
+
+ return 0;
+}
+
+int nghttp3_http_on_response_headers(nghttp3_http_state *http) {
+ if ((http->flags & NGHTTP3_HTTP_FLAG__STATUS) == 0) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_HEADER;
+ }
+
+ if (http->status_code / 100 == 1) {
+ /* non-final response */
+ http->flags = (http->flags & NGHTTP3_HTTP_FLAG_METH_ALL) |
+ NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
+ http->content_length = -1;
+ http->status_code = -1;
+ return 0;
+ }
+
+ http->flags &= ~NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE;
+
+ if (!expect_response_body(http)) {
+ http->content_length = 0;
+ } else if (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) {
+ http->content_length = -1;
+ }
+
+ return 0;
+}
+
+int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream) {
+ if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
+ (stream->rx.http.content_length != -1 &&
+ stream->rx.http.content_length != stream->rx.http.recv_content_length)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+
+ return 0;
+}
+
+int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n) {
+ stream->rx.http.recv_content_length += (int64_t)n;
+
+ if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) ||
+ (stream->rx.http.content_length != -1 &&
+ stream->rx.http.recv_content_length > stream->rx.http.content_length)) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+
+ return 0;
+}
+
+void nghttp3_http_record_request_method(nghttp3_stream *stream,
+ const nghttp3_nv *nva, size_t nvlen) {
+ size_t i;
+ const nghttp3_nv *nv;
+
+ /* TODO we should do this strictly. */
+ for (i = 0; i < nvlen; ++i) {
+ nv = &nva[i];
+ if (!(nv->namelen == 7 && nv->name[6] == 'd' &&
+ memcmp(":metho", nv->name, nv->namelen - 1) == 0)) {
+ continue;
+ }
+ if (lstreq("CONNECT", nv->value, nv->valuelen)) {
+ stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT;
+ return;
+ }
+ if (lstreq("HEAD", nv->value, nv->valuelen)) {
+ stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_HEAD;
+ return;
+ }
+ return;
+ }
+}
+
+/* Generated by gennmchartbl.py */
+static const int VALID_HD_NAME_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
+ 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
+ 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */,
+ 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
+ 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
+ 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
+ 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
+ 0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */,
+ 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */,
+ 0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */,
+ 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
+ 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
+ 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */,
+ 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */,
+ 0 /* @ */, 0 /* A */, 0 /* B */, 0 /* C */,
+ 0 /* D */, 0 /* E */, 0 /* F */, 0 /* G */,
+ 0 /* H */, 0 /* I */, 0 /* J */, 0 /* K */,
+ 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */,
+ 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */,
+ 0 /* T */, 0 /* U */, 0 /* V */, 0 /* W */,
+ 0 /* X */, 0 /* Y */, 0 /* Z */, 0 /* [ */,
+ 0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */,
+ 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
+ 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
+ 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
+ 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
+ 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */,
+ 1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */,
+ 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */,
+ 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */,
+ 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */,
+ 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */,
+ 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */,
+ 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */,
+ 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */,
+ 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */,
+ 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */,
+ 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */,
+ 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */,
+ 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */,
+ 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */,
+ 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */,
+ 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */,
+ 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */,
+ 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */,
+ 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */,
+ 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */,
+ 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */,
+ 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */,
+ 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */,
+ 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */,
+ 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */,
+ 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */,
+ 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */,
+ 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */,
+ 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */,
+ 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */,
+ 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */,
+ 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */,
+ 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */
+};
+
+int nghttp3_check_header_name(const uint8_t *name, size_t len) {
+ const uint8_t *last;
+ if (len == 0) {
+ return 0;
+ }
+ if (*name == ':') {
+ if (len == 1) {
+ return 0;
+ }
+ ++name;
+ --len;
+ }
+ for (last = name + len; name != last; ++name) {
+ if (!VALID_HD_NAME_CHARS[*name]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Generated by genvchartbl.py */
+static const int VALID_HD_VALUE_CHARS[] = {
+ 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */,
+ 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */,
+ 0 /* BS */, 1 /* HT */, 0 /* LF */, 0 /* VT */,
+ 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */,
+ 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */,
+ 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */,
+ 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */,
+ 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */,
+ 1 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */,
+ 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */,
+ 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */,
+ 1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */,
+ 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */,
+ 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
+ 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
+ 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */,
+ 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
+ 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
+ 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
+ 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
+ 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */,
+ 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */,
+ 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */,
+ 1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */,
+ 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */,
+ 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */,
+ 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */,
+ 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */,
+ 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */,
+ 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */,
+ 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */,
+ 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */,
+ 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,
+ 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,
+ 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,
+ 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,
+ 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,
+ 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,
+ 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,
+ 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,
+ 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,
+ 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,
+ 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,
+ 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,
+ 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,
+ 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,
+ 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,
+ 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,
+ 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,
+ 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,
+ 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,
+ 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,
+ 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */,
+ 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */,
+ 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,
+ 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */,
+ 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */,
+ 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */,
+ 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */,
+ 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,
+ 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */,
+ 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */,
+ 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */,
+ 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */
+};
+
+int nghttp3_check_header_value(const uint8_t *value, size_t len) {
+ const uint8_t *last;
+
+ switch (len) {
+ case 0:
+ return 1;
+ case 1:
+ return !(*value == ' ' || *value == '\t');
+ default:
+ if (*value == ' ' || *value == '\t' || *(value + len - 1) == ' ' ||
+ *(value + len - 1) == '\t') {
+ return 0;
+ }
+ }
+
+ for (last = value + len; value != last; ++value) {
+ if (!VALID_HD_VALUE_CHARS[*value]) {
+ return 0;
+ }
+ }
+ return 1;
+}
diff --git a/lib/nghttp3_http.h b/lib/nghttp3_http.h
new file mode 100644
index 0000000..1617348
--- /dev/null
+++ b/lib/nghttp3_http.h
@@ -0,0 +1,202 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2015 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_HTTP_H
+#define NGHTTP3_HTTP_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+typedef struct nghttp3_stream nghttp3_stream;
+
+typedef struct nghttp3_http_state nghttp3_http_state;
+
+/* HTTP related flags to enforce HTTP semantics */
+
+/* NGHTTP3_HTTP_FLAG_NONE indicates that no flag is set. */
+#define NGHTTP3_HTTP_FLAG_NONE 0x00u
+/* header field seen so far */
+#define NGHTTP3_HTTP_FLAG__AUTHORITY 0x01u
+#define NGHTTP3_HTTP_FLAG__PATH 0x02u
+#define NGHTTP3_HTTP_FLAG__METHOD 0x04u
+#define NGHTTP3_HTTP_FLAG__SCHEME 0x08u
+/* host is not pseudo header, but we require either host or
+ :authority */
+#define NGHTTP3_HTTP_FLAG_HOST 0x10u
+#define NGHTTP3_HTTP_FLAG__STATUS 0x20u
+/* required header fields for HTTP request except for CONNECT
+ method. */
+#define NGHTTP3_HTTP_FLAG_REQ_HEADERS \
+ (NGHTTP3_HTTP_FLAG__METHOD | NGHTTP3_HTTP_FLAG__PATH | \
+ NGHTTP3_HTTP_FLAG__SCHEME)
+#define NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED 0x40u
+/* HTTP method flags */
+#define NGHTTP3_HTTP_FLAG_METH_CONNECT 0x80u
+#define NGHTTP3_HTTP_FLAG_METH_HEAD 0x0100u
+#define NGHTTP3_HTTP_FLAG_METH_OPTIONS 0x0200u
+#define NGHTTP3_HTTP_FLAG_METH_ALL \
+ (NGHTTP3_HTTP_FLAG_METH_CONNECT | NGHTTP3_HTTP_FLAG_METH_HEAD | \
+ NGHTTP3_HTTP_FLAG_METH_OPTIONS)
+/* :path category */
+/* path starts with "/" */
+#define NGHTTP3_HTTP_FLAG_PATH_REGULAR 0x0400u
+/* path "*" */
+#define NGHTTP3_HTTP_FLAG_PATH_ASTERISK 0x0800u
+/* scheme */
+/* "http" or "https" scheme */
+#define NGHTTP3_HTTP_FLAG_SCHEME_HTTP 0x1000u
+/* set if final response is expected */
+#define NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE 0x2000u
+/* NGHTTP3_HTTP_FLAG__PROTOCOL is set when :protocol pseudo header
+ field is seen. */
+#define NGHTTP3_HTTP_FLAG__PROTOCOL 0x4000u
+/* NGHTTP3_HTTP_FLAG_PRIORITY is set when priority header field is
+ processed. */
+#define NGHTTP3_HTTP_FLAG_PRIORITY 0x8000u
+/* NGHTTP3_HTTP_FLAG_BAD_PRIORITY is set when an error is encountered
+ while parsing priority header field. */
+#define NGHTTP3_HTTP_FLAG_BAD_PRIORITY 0x010000u
+
+/*
+ * This function is called when HTTP header field |nv| received for
+ * |http|. This function will validate |nv| against the current state
+ * of stream. Pass nonzero if this is request headers. Pass nonzero
+ * to |trailers| if |nv| is included in trailers. |connect_protocol|
+ * is nonzero if Extended CONNECT Method is enabled.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_HEADER
+ * Invalid HTTP header field was received.
+ * NGHTTP3_ERR_REMOVE_HTTP_HEADER
+ * Invalid HTTP header field was received but it can be treated as
+ * if it was not received because of compatibility reasons.
+ */
+int nghttp3_http_on_header(nghttp3_http_state *http, nghttp3_qpack_nv *nv,
+ int request, int trailers, int connect_protocol);
+
+/*
+ * This function is called when request header is received. This
+ * function performs validation and returns 0 if it succeeds, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_HEADER
+ * Required HTTP header field was not received; or an invalid
+ * header field was received.
+ */
+int nghttp3_http_on_request_headers(nghttp3_http_state *http);
+
+/*
+ * This function is called when response header is received. This
+ * function performs validation and returns 0 if it succeeds, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_HEADER
+ * Required HTTP header field was not received; or an invalid
+ * header field was received.
+ */
+int nghttp3_http_on_response_headers(nghttp3_http_state *http);
+
+/*
+ * This function is called when read side stream is closed. This
+ * function performs validation and returns 0 if it succeeds, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING
+ * HTTP messaging is violated.
+ */
+int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream);
+
+/*
+ * This function is called when chunk of data is received. This
+ * function performs validation and returns 0 if it succeeds, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING
+ * HTTP messaging is violated.
+ */
+int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n);
+
+/*
+ * This function inspects header fields in |nva| of length |nvlen| and
+ * records its method in stream->http_flags.
+ */
+void nghttp3_http_record_request_method(nghttp3_stream *stream,
+ const nghttp3_nv *nva, size_t nvlen);
+
+/*
+ * RFC 8941 Structured Field Values.
+ */
+typedef enum nghttp3_sf_value_type {
+ NGHTTP3_SF_VALUE_TYPE_BOOLEAN,
+ NGHTTP3_SF_VALUE_TYPE_INTEGER,
+ NGHTTP3_SF_VALUE_TYPE_DECIMAL,
+ NGHTTP3_SF_VALUE_TYPE_STRING,
+ NGHTTP3_SF_VALUE_TYPE_TOKEN,
+ NGHTTP3_SF_VALUE_TYPE_BYTESEQ,
+ NGHTTP3_SF_VALUE_TYPE_INNER_LIST,
+} nghttp3_sf_value_type;
+
+/*
+ * nghttp3_sf_value stores Structured Field Values item. For Inner
+ * List, only type is set to NGHTTP3_SF_VALUE_TYPE_INNER_LIST.
+ */
+typedef struct nghttp3_sf_value {
+ uint8_t type;
+ union {
+ int b;
+ int64_t i;
+ double d;
+ struct {
+ const uint8_t *base;
+ size_t len;
+ } s;
+ };
+} nghttp3_sf_value;
+
+/*
+ * nghttp3_sf_parse_item parses the input sequence [|begin|, |end|)
+ * and stores the parsed an Item in |dest|. It returns the number of
+ * bytes consumed if it succeeds, or -1. This function is declared
+ * here for unit tests.
+ */
+nghttp3_ssize nghttp3_sf_parse_item(nghttp3_sf_value *dest,
+ const uint8_t *begin, const uint8_t *end);
+
+/*
+ * nghttp3_sf_parse_inner_list parses the input sequence [|begin|, |end|)
+ * and stores the parsed an Inner List in |dest|. It returns the number of
+ * bytes consumed if it succeeds, or -1. This function is declared
+ * here for unit tests.
+ */
+nghttp3_ssize nghttp3_sf_parse_inner_list(nghttp3_sf_value *dest,
+ const uint8_t *begin,
+ const uint8_t *end);
+
+#endif /* NGHTTP3_HTTP_H */
diff --git a/lib/nghttp3_idtr.c b/lib/nghttp3_idtr.c
new file mode 100644
index 0000000..dc34841
--- /dev/null
+++ b/lib/nghttp3_idtr.c
@@ -0,0 +1,80 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_idtr.h"
+
+#include <assert.h>
+
+void nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem) {
+ nghttp3_gaptr_init(&idtr->gap, mem);
+
+ idtr->server = server;
+}
+
+void nghttp3_idtr_free(nghttp3_idtr *idtr) {
+ if (idtr == NULL) {
+ return;
+ }
+
+ nghttp3_gaptr_free(&idtr->gap);
+}
+
+/*
+ * id_from_stream_id translates |stream_id| to id space used by
+ * nghttp3_idtr.
+ */
+static uint64_t id_from_stream_id(int64_t stream_id) {
+ return (uint64_t)(stream_id >> 2);
+}
+
+int nghttp3_idtr_open(nghttp3_idtr *idtr, int64_t stream_id) {
+ uint64_t q;
+
+ assert((idtr->server && (stream_id % 2)) ||
+ (!idtr->server && (stream_id % 2)) == 0);
+
+ q = id_from_stream_id(stream_id);
+
+ if (nghttp3_gaptr_is_pushed(&idtr->gap, q, 1)) {
+ return NGHTTP3_ERR_STREAM_IN_USE;
+ }
+
+ return nghttp3_gaptr_push(&idtr->gap, q, 1);
+}
+
+int nghttp3_idtr_is_open(nghttp3_idtr *idtr, int64_t stream_id) {
+ uint64_t q;
+
+ assert((idtr->server && (stream_id % 2)) ||
+ (!idtr->server && (stream_id % 2)) == 0);
+
+ q = id_from_stream_id(stream_id);
+
+ return nghttp3_gaptr_is_pushed(&idtr->gap, q, 1);
+}
+
+uint64_t nghttp3_idtr_first_gap(nghttp3_idtr *idtr) {
+ return nghttp3_gaptr_first_gap_offset(&idtr->gap);
+}
diff --git a/lib/nghttp3_idtr.h b/lib/nghttp3_idtr.h
new file mode 100644
index 0000000..ea3346c
--- /dev/null
+++ b/lib/nghttp3_idtr.h
@@ -0,0 +1,90 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_IDTR_H
+#define NGHTTP3_IDTR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+#include "nghttp3_gaptr.h"
+
+/*
+ * nghttp3_idtr tracks the usage of stream ID.
+ */
+typedef struct nghttp3_idtr {
+ /* gap maintains the range of ID which is not used yet. Initially,
+ its range is [0, UINT64_MAX). */
+ nghttp3_gaptr gap;
+ /* server is nonzero if this object records server initiated stream
+ ID. */
+ int server;
+} nghttp3_idtr;
+
+/*
+ * nghttp3_idtr_init initializes |idtr|.
+ *
+ * If this object records server initiated ID (even number), set
+ * |server| to nonzero.
+ */
+void nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem);
+
+/*
+ * nghttp3_idtr_free frees resources allocated for |idtr|.
+ */
+void nghttp3_idtr_free(nghttp3_idtr *idtr);
+
+/*
+ * nghttp3_idtr_open claims that |stream_id| is in used.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_STREAM_IN_USE
+ * ID has already been used.
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_idtr_open(nghttp3_idtr *idtr, int64_t stream_id);
+
+/*
+ * nghttp3_idtr_open tells whether ID |stream_id| is in used or not.
+ *
+ * It returns nonzero if |stream_id| is used.
+ */
+int nghttp3_idtr_is_open(nghttp3_idtr *idtr, int64_t stream_id);
+
+/*
+ * nghttp3_idtr_first_gap returns the first id of first gap. If there
+ * is no gap, it returns UINT64_MAX. The returned id is an id space
+ * used in this object internally, and not stream ID.
+ */
+uint64_t nghttp3_idtr_first_gap(nghttp3_idtr *idtr);
+
+#endif /* NGHTTP3_IDTR_H */
diff --git a/lib/nghttp3_ksl.c b/lib/nghttp3_ksl.c
new file mode 100644
index 0000000..adea677
--- /dev/null
+++ b/lib/nghttp3_ksl.c
@@ -0,0 +1,827 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_ksl.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "nghttp3_macro.h"
+#include "nghttp3_mem.h"
+#include "nghttp3_range.h"
+
+static nghttp3_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}};
+
+static size_t ksl_nodelen(size_t keylen) {
+ return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0xfu) &
+ ~(uintptr_t)0xfu;
+}
+
+static size_t ksl_blklen(size_t nodelen) {
+ return sizeof(nghttp3_ksl_blk) + nodelen * NGHTTP3_KSL_MAX_NBLK -
+ sizeof(uint64_t);
+}
+
+/*
+ * ksl_node_set_key sets |key| to |node|.
+ */
+static void ksl_node_set_key(nghttp3_ksl *ksl, nghttp3_ksl_node *node,
+ const void *key) {
+ memcpy(node->key, key, ksl->keylen);
+}
+
+void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar,
+ size_t keylen, const nghttp3_mem *mem) {
+ size_t nodelen = ksl_nodelen(keylen);
+
+ nghttp3_objalloc_init(&ksl->blkalloc,
+ ((ksl_blklen(nodelen) + 0xfu) & ~(uintptr_t)0xfu) * 8,
+ mem);
+
+ ksl->head = NULL;
+ ksl->front = ksl->back = NULL;
+ ksl->compar = compar;
+ ksl->keylen = keylen;
+ ksl->nodelen = nodelen;
+ ksl->n = 0;
+}
+
+static nghttp3_ksl_blk *ksl_blk_objalloc_new(nghttp3_ksl *ksl) {
+ return nghttp3_objalloc_ksl_blk_len_get(&ksl->blkalloc,
+ ksl_blklen(ksl->nodelen));
+}
+
+static void ksl_blk_objalloc_del(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) {
+ nghttp3_objalloc_ksl_blk_release(&ksl->blkalloc, blk);
+}
+
+static int ksl_head_init(nghttp3_ksl *ksl) {
+ nghttp3_ksl_blk *head = ksl_blk_objalloc_new(ksl);
+ if (!head) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ head->next = head->prev = NULL;
+ head->n = 0;
+ head->leaf = 1;
+
+ ksl->head = head;
+ ksl->front = ksl->back = head;
+
+ return 0;
+}
+
+#ifdef NOMEMPOOL
+/*
+ * ksl_free_blk frees |blk| recursively.
+ */
+static void ksl_free_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) {
+ size_t i;
+
+ if (!blk->leaf) {
+ for (i = 0; i < blk->n; ++i) {
+ ksl_free_blk(ksl, nghttp3_ksl_nth_node(ksl, blk, i)->blk);
+ }
+ }
+
+ ksl_blk_objalloc_del(ksl, blk);
+}
+#endif /* NOMEMPOOL */
+
+void nghttp3_ksl_free(nghttp3_ksl *ksl) {
+ if (!ksl || !ksl->head) {
+ return;
+ }
+
+#ifdef NOMEMPOOL
+ ksl_free_blk(ksl, ksl->head);
+#endif /* NOMEMPOOL */
+
+ nghttp3_objalloc_free(&ksl->blkalloc);
+}
+
+/*
+ * ksl_split_blk splits |blk| into 2 nghttp3_ksl_blk objects. The new
+ * nghttp3_ksl_blk is always the "right" block.
+ *
+ * It returns the pointer to the nghttp3_ksl_blk created which is the
+ * located at the right of |blk|, or NULL which indicates out of
+ * memory error.
+ */
+static nghttp3_ksl_blk *ksl_split_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) {
+ nghttp3_ksl_blk *rblk;
+
+ rblk = ksl_blk_objalloc_new(ksl);
+ if (rblk == NULL) {
+ return NULL;
+ }
+
+ rblk->next = blk->next;
+ blk->next = rblk;
+ if (rblk->next) {
+ rblk->next->prev = rblk;
+ } else if (ksl->back == blk) {
+ ksl->back = rblk;
+ }
+ rblk->prev = blk;
+ rblk->leaf = blk->leaf;
+
+ rblk->n = blk->n / 2;
+
+ memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n),
+ ksl->nodelen * rblk->n);
+
+ blk->n -= rblk->n;
+
+ assert(blk->n >= NGHTTP3_KSL_MIN_NBLK);
+ assert(rblk->n >= NGHTTP3_KSL_MIN_NBLK);
+
+ return rblk;
+}
+
+/*
+ * ksl_split_node splits a node included in |blk| at the position |i|
+ * into 2 adjacent nodes. The new node is always inserted at the
+ * position |i+1|.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int ksl_split_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) {
+ nghttp3_ksl_node *node;
+ nghttp3_ksl_blk *lblk = nghttp3_ksl_nth_node(ksl, blk, i)->blk, *rblk;
+
+ rblk = ksl_split_blk(ksl, lblk);
+ if (rblk == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ memmove(blk->nodes + (i + 2) * ksl->nodelen,
+ blk->nodes + (i + 1) * ksl->nodelen,
+ ksl->nodelen * (blk->n - (i + 1)));
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i + 1);
+ node->blk = rblk;
+ ++blk->n;
+ ksl_node_set_key(ksl, node,
+ nghttp3_ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+ ksl_node_set_key(ksl, node,
+ nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+
+ return 0;
+}
+
+/*
+ * ksl_split_head splits a head (root) block. It increases the height
+ * of skip list by 1.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int ksl_split_head(nghttp3_ksl *ksl) {
+ nghttp3_ksl_blk *rblk = NULL, *lblk, *nhead = NULL;
+ nghttp3_ksl_node *node;
+
+ rblk = ksl_split_blk(ksl, ksl->head);
+ if (rblk == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ lblk = ksl->head;
+
+ nhead = ksl_blk_objalloc_new(ksl);
+ if (nhead == NULL) {
+ ksl_blk_objalloc_del(ksl, rblk);
+ return NGHTTP3_ERR_NOMEM;
+ }
+ nhead->next = nhead->prev = NULL;
+ nhead->n = 2;
+ nhead->leaf = 0;
+
+ node = nghttp3_ksl_nth_node(ksl, nhead, 0);
+ ksl_node_set_key(ksl, node,
+ nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ node->blk = lblk;
+
+ node = nghttp3_ksl_nth_node(ksl, nhead, 1);
+ ksl_node_set_key(ksl, node,
+ nghttp3_ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
+ node->blk = rblk;
+
+ ksl->head = nhead;
+
+ return 0;
+}
+
+/*
+ * insert_node inserts a node whose key is |key| with the associated
+ * |data| at the index of |i|. This function assumes that the number
+ * of nodes contained by |blk| is strictly less than
+ * NGHTTP3_KSL_MAX_NBLK.
+ */
+static void ksl_insert_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i,
+ const nghttp3_ksl_key *key, void *data) {
+ nghttp3_ksl_node *node;
+
+ assert(blk->n < NGHTTP3_KSL_MAX_NBLK);
+
+ memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen,
+ ksl->nodelen * (blk->n - i));
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+ ksl_node_set_key(ksl, node, key);
+ node->data = data;
+
+ ++blk->n;
+}
+
+static size_t ksl_bsearch(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk,
+ const nghttp3_ksl_key *key,
+ nghttp3_ksl_compar compar) {
+ size_t i;
+ nghttp3_ksl_node *node;
+
+ for (i = 0, node = (nghttp3_ksl_node *)(void *)blk->nodes;
+ i < blk->n && compar((nghttp3_ksl_key *)node->key, key);
+ ++i, node = (nghttp3_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen))
+ ;
+
+ return i;
+}
+
+int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_key *key, void *data) {
+ nghttp3_ksl_blk *blk;
+ nghttp3_ksl_node *node;
+ size_t i;
+ int rv;
+
+ if (!ksl->head) {
+ rv = ksl_head_init(ksl);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ blk = ksl->head;
+
+ if (blk->n == NGHTTP3_KSL_MAX_NBLK) {
+ rv = ksl_split_head(ksl);
+ if (rv != 0) {
+ return rv;
+ }
+ blk = ksl->head;
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (blk->leaf) {
+ if (i < blk->n &&
+ !ksl->compar(key, nghttp3_ksl_nth_node(ksl, blk, i)->key)) {
+ if (it) {
+ *it = nghttp3_ksl_end(ksl);
+ }
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+ ksl_insert_node(ksl, blk, i, key, data);
+ ++ksl->n;
+ if (it) {
+ nghttp3_ksl_it_init(it, ksl, blk, i);
+ }
+ return 0;
+ }
+
+ if (i == blk->n) {
+ /* This insertion extends the largest key in this subtree. */
+ for (; !blk->leaf;) {
+ node = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1);
+ if (node->blk->n == NGHTTP3_KSL_MAX_NBLK) {
+ rv = ksl_split_node(ksl, blk, blk->n - 1);
+ if (rv != 0) {
+ return rv;
+ }
+ node = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1);
+ }
+ ksl_node_set_key(ksl, node, key);
+ blk = node->blk;
+ }
+ ksl_insert_node(ksl, blk, blk->n, key, data);
+ ++ksl->n;
+ if (it) {
+ nghttp3_ksl_it_init(it, ksl, blk, blk->n - 1);
+ }
+ return 0;
+ }
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+
+ if (node->blk->n == NGHTTP3_KSL_MAX_NBLK) {
+ rv = ksl_split_node(ksl, blk, i);
+ if (rv != 0) {
+ return rv;
+ }
+ if (ksl->compar((nghttp3_ksl_key *)node->key, key)) {
+ node = nghttp3_ksl_nth_node(ksl, blk, i + 1);
+ if (ksl->compar((nghttp3_ksl_key *)node->key, key)) {
+ ksl_node_set_key(ksl, node, key);
+ }
+ }
+ }
+
+ blk = node->blk;
+ }
+}
+
+/*
+ * ksl_remove_node removes the node included in |blk| at the index of
+ * |i|.
+ */
+static void ksl_remove_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) {
+ memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen,
+ ksl->nodelen * (blk->n - (i + 1)));
+
+ --blk->n;
+}
+
+/*
+ * ksl_merge_node merges 2 nodes which are the nodes at the index of
+ * |i| and |i + 1|.
+ *
+ * If |blk| is the direct descendant of head (root) block and the head
+ * block contains just 2 nodes, the merged block becomes head block,
+ * which decreases the height of |ksl| by 1.
+ *
+ * This function returns the pointer to the merged block.
+ */
+static nghttp3_ksl_blk *ksl_merge_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk,
+ size_t i) {
+ nghttp3_ksl_blk *lblk, *rblk;
+
+ assert(i + 1 < blk->n);
+
+ lblk = nghttp3_ksl_nth_node(ksl, blk, i)->blk;
+ rblk = nghttp3_ksl_nth_node(ksl, blk, i + 1)->blk;
+
+ assert(lblk->n + rblk->n < NGHTTP3_KSL_MAX_NBLK);
+
+ memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes,
+ ksl->nodelen * rblk->n);
+
+ lblk->n += rblk->n;
+ lblk->next = rblk->next;
+ if (lblk->next) {
+ lblk->next->prev = lblk;
+ } else if (ksl->back == rblk) {
+ ksl->back = lblk;
+ }
+
+ ksl_blk_objalloc_del(ksl, rblk);
+
+ if (ksl->head == blk && blk->n == 2) {
+ ksl_blk_objalloc_del(ksl, ksl->head);
+ ksl->head = lblk;
+ } else {
+ ksl_remove_node(ksl, blk, i + 1);
+ ksl_node_set_key(ksl, nghttp3_ksl_nth_node(ksl, blk, i),
+ nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ }
+
+ return lblk;
+}
+
+/*
+ * ksl_shift_left moves the first nodes in blk->nodes[i]->blk->nodes
+ * to blk->nodes[i - 1]->blk->nodes in a manner that they have the
+ * same amount of nodes as much as possible.
+ */
+static void ksl_shift_left(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) {
+ nghttp3_ksl_node *lnode, *rnode;
+ size_t n;
+
+ assert(i > 0);
+
+ lnode = nghttp3_ksl_nth_node(ksl, blk, i - 1);
+ rnode = nghttp3_ksl_nth_node(ksl, blk, i);
+
+ assert(lnode->blk->n < NGHTTP3_KSL_MAX_NBLK);
+ assert(rnode->blk->n > NGHTTP3_KSL_MIN_NBLK);
+
+ n = (lnode->blk->n + rnode->blk->n + 1) / 2 - lnode->blk->n;
+
+ assert(n > 0);
+ assert(lnode->blk->n <= NGHTTP3_KSL_MAX_NBLK - n);
+ assert(rnode->blk->n >= NGHTTP3_KSL_MIN_NBLK + n);
+
+ memcpy(lnode->blk->nodes + ksl->nodelen * lnode->blk->n, rnode->blk->nodes,
+ ksl->nodelen * n);
+
+ lnode->blk->n += (uint32_t)n;
+ rnode->blk->n -= (uint32_t)n;
+
+ ksl_node_set_key(
+ ksl, lnode,
+ nghttp3_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key);
+
+ memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen * n,
+ ksl->nodelen * rnode->blk->n);
+}
+
+/*
+ * ksl_shift_right moves the last nodes in blk->nodes[i]->blk->nodes
+ * to blk->nodes[i + 1]->blk->nodes in a manner that they have the
+ * same amount of nodes as much as possible..
+ */
+static void ksl_shift_right(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) {
+ nghttp3_ksl_node *lnode, *rnode;
+ size_t n;
+
+ assert(i < blk->n - 1);
+
+ lnode = nghttp3_ksl_nth_node(ksl, blk, i);
+ rnode = nghttp3_ksl_nth_node(ksl, blk, i + 1);
+
+ assert(lnode->blk->n > NGHTTP3_KSL_MIN_NBLK);
+ assert(rnode->blk->n < NGHTTP3_KSL_MAX_NBLK);
+
+ n = (lnode->blk->n + rnode->blk->n + 1) / 2 - rnode->blk->n;
+
+ assert(n > 0);
+ assert(lnode->blk->n >= NGHTTP3_KSL_MIN_NBLK + n);
+ assert(rnode->blk->n <= NGHTTP3_KSL_MAX_NBLK - n);
+
+ memmove(rnode->blk->nodes + ksl->nodelen * n, rnode->blk->nodes,
+ ksl->nodelen * rnode->blk->n);
+
+ rnode->blk->n += (uint32_t)n;
+ lnode->blk->n -= (uint32_t)n;
+
+ memcpy(rnode->blk->nodes, lnode->blk->nodes + ksl->nodelen * lnode->blk->n,
+ ksl->nodelen * n);
+
+ ksl_node_set_key(
+ ksl, lnode,
+ nghttp3_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key);
+}
+
+/*
+ * key_equal returns nonzero if |lhs| and |rhs| are equal using the
+ * function |compar|.
+ */
+static int key_equal(nghttp3_ksl_compar compar, const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs) {
+ return !compar(lhs, rhs) && !compar(rhs, lhs);
+}
+
+int nghttp3_ksl_remove_hint(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_it *hint,
+ const nghttp3_ksl_key *key) {
+ nghttp3_ksl_blk *blk = hint->blk;
+
+ assert(ksl->head);
+
+ if (blk->n <= NGHTTP3_KSL_MIN_NBLK) {
+ return nghttp3_ksl_remove(ksl, it, key);
+ }
+
+ ksl_remove_node(ksl, blk, hint->i);
+
+ --ksl->n;
+
+ if (it) {
+ if (hint->i == blk->n && blk->next) {
+ nghttp3_ksl_it_init(it, ksl, blk->next, 0);
+ } else {
+ nghttp3_ksl_it_init(it, ksl, blk, hint->i);
+ }
+ }
+
+ return 0;
+}
+
+int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_key *key) {
+ nghttp3_ksl_blk *blk = ksl->head;
+ nghttp3_ksl_node *node;
+ size_t i;
+
+ if (!ksl->head) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ if (!blk->leaf && blk->n == 2 &&
+ nghttp3_ksl_nth_node(ksl, blk, 0)->blk->n == NGHTTP3_KSL_MIN_NBLK &&
+ nghttp3_ksl_nth_node(ksl, blk, 1)->blk->n == NGHTTP3_KSL_MIN_NBLK) {
+ blk = ksl_merge_node(ksl, ksl->head, 0);
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (i == blk->n) {
+ if (it) {
+ *it = nghttp3_ksl_end(ksl);
+ }
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ if (blk->leaf) {
+ if (ksl->compar(key, nghttp3_ksl_nth_node(ksl, blk, i)->key)) {
+ if (it) {
+ *it = nghttp3_ksl_end(ksl);
+ }
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+ ksl_remove_node(ksl, blk, i);
+ --ksl->n;
+ if (it) {
+ if (blk->n == i && blk->next) {
+ nghttp3_ksl_it_init(it, ksl, blk->next, 0);
+ } else {
+ nghttp3_ksl_it_init(it, ksl, blk, i);
+ }
+ }
+ return 0;
+ }
+
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+
+ if (node->blk->n > NGHTTP3_KSL_MIN_NBLK) {
+ blk = node->blk;
+ continue;
+ }
+
+ assert(node->blk->n == NGHTTP3_KSL_MIN_NBLK);
+
+ if (i + 1 < blk->n &&
+ nghttp3_ksl_nth_node(ksl, blk, i + 1)->blk->n > NGHTTP3_KSL_MIN_NBLK) {
+ ksl_shift_left(ksl, blk, i + 1);
+ blk = node->blk;
+ continue;
+ }
+
+ if (i > 0 &&
+ nghttp3_ksl_nth_node(ksl, blk, i - 1)->blk->n > NGHTTP3_KSL_MIN_NBLK) {
+ ksl_shift_right(ksl, blk, i - 1);
+ blk = node->blk;
+ continue;
+ }
+
+ if (i + 1 < blk->n) {
+ blk = ksl_merge_node(ksl, blk, i);
+ continue;
+ }
+
+ assert(i > 0);
+
+ blk = ksl_merge_node(ksl, blk, i - 1);
+ }
+}
+
+nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl,
+ const nghttp3_ksl_key *key) {
+ nghttp3_ksl_blk *blk = ksl->head;
+ nghttp3_ksl_it it;
+ size_t i;
+
+ if (!blk) {
+ nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
+ return it;
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (blk->leaf) {
+ if (i == blk->n && blk->next) {
+ blk = blk->next;
+ i = 0;
+ }
+ nghttp3_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+
+ if (i == blk->n) {
+ /* This happens if descendant has smaller key. Fast forward to
+ find last node in this subtree. */
+ for (; !blk->leaf; blk = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1)->blk)
+ ;
+ if (blk->next) {
+ blk = blk->next;
+ i = 0;
+ } else {
+ i = blk->n;
+ }
+ nghttp3_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+ blk = nghttp3_ksl_nth_node(ksl, blk, i)->blk;
+ }
+}
+
+nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl,
+ const nghttp3_ksl_key *key,
+ nghttp3_ksl_compar compar) {
+ nghttp3_ksl_blk *blk = ksl->head;
+ nghttp3_ksl_it it;
+ size_t i;
+
+ if (!blk) {
+ nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
+ return it;
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, compar);
+
+ if (blk->leaf) {
+ if (i == blk->n && blk->next) {
+ blk = blk->next;
+ i = 0;
+ }
+ nghttp3_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+
+ if (i == blk->n) {
+ /* This happens if descendant has smaller key. Fast forward to
+ find last node in this subtree. */
+ for (; !blk->leaf; blk = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1)->blk)
+ ;
+ if (blk->next) {
+ blk = blk->next;
+ i = 0;
+ } else {
+ i = blk->n;
+ }
+ nghttp3_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+ blk = nghttp3_ksl_nth_node(ksl, blk, i)->blk;
+ }
+}
+
+void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key,
+ const nghttp3_ksl_key *new_key) {
+ nghttp3_ksl_blk *blk = ksl->head;
+ nghttp3_ksl_node *node;
+ size_t i;
+
+ assert(ksl->head);
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, old_key, ksl->compar);
+
+ assert(i < blk->n);
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+
+ if (blk->leaf) {
+ assert(key_equal(ksl->compar, (nghttp3_ksl_key *)node->key, old_key));
+ ksl_node_set_key(ksl, node, new_key);
+ return;
+ }
+
+ if (key_equal(ksl->compar, (nghttp3_ksl_key *)node->key, old_key) ||
+ ksl->compar((nghttp3_ksl_key *)node->key, new_key)) {
+ ksl_node_set_key(ksl, node, new_key);
+ }
+
+ blk = node->blk;
+ }
+}
+
+static void ksl_print(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t level) {
+ size_t i;
+ nghttp3_ksl_node *node;
+
+ fprintf(stderr, "LV=%zu n=%u\n", level, blk->n);
+
+ if (blk->leaf) {
+ for (i = 0; i < blk->n; ++i) {
+ node = nghttp3_ksl_nth_node(ksl, blk, i);
+ fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key);
+ }
+ fprintf(stderr, "\n");
+ return;
+ }
+
+ for (i = 0; i < blk->n; ++i) {
+ ksl_print(ksl, nghttp3_ksl_nth_node(ksl, blk, i)->blk, level + 1);
+ }
+}
+
+size_t nghttp3_ksl_len(nghttp3_ksl *ksl) { return ksl->n; }
+
+void nghttp3_ksl_clear(nghttp3_ksl *ksl) {
+ if (!ksl->head) {
+ return;
+ }
+
+#ifdef NOMEMPOOL
+ ksl_free_blk(ksl, ksl->head);
+#endif /* NOMEMPOOL */
+
+ ksl->front = ksl->back = ksl->head = NULL;
+ ksl->n = 0;
+
+ nghttp3_objalloc_clear(&ksl->blkalloc);
+}
+
+void nghttp3_ksl_print(nghttp3_ksl *ksl) {
+ if (!ksl->head) {
+ return;
+ }
+
+ ksl_print(ksl, ksl->head, 0);
+}
+
+nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl) {
+ nghttp3_ksl_it it;
+
+ if (ksl->head) {
+ nghttp3_ksl_it_init(&it, ksl, ksl->front, 0);
+ } else {
+ nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
+ }
+
+ return it;
+}
+
+nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl) {
+ nghttp3_ksl_it it;
+
+ if (ksl->head) {
+ nghttp3_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
+ } else {
+ nghttp3_ksl_it_init(&it, ksl, &null_blk, 0);
+ }
+
+ return it;
+}
+
+void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl,
+ nghttp3_ksl_blk *blk, size_t i) {
+ it->ksl = ksl;
+ it->blk = blk;
+ it->i = i;
+}
+
+void nghttp3_ksl_it_prev(nghttp3_ksl_it *it) {
+ assert(!nghttp3_ksl_it_begin(it));
+
+ if (it->i == 0) {
+ it->blk = it->blk->prev;
+ it->i = it->blk->n - 1;
+ } else {
+ --it->i;
+ }
+}
+
+int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it) {
+ return it->i == 0 && it->blk->prev == NULL;
+}
+
+int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs) {
+ const nghttp3_range *a = lhs, *b = rhs;
+ return a->begin < b->begin;
+}
+
+int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs) {
+ const nghttp3_range *a = lhs, *b = rhs;
+ return a->begin < b->begin &&
+ !(nghttp3_max(a->begin, b->begin) < nghttp3_min(a->end, b->end));
+}
diff --git a/lib/nghttp3_ksl.h b/lib/nghttp3_ksl.h
new file mode 100644
index 0000000..0bc10e8
--- /dev/null
+++ b/lib/nghttp3_ksl.h
@@ -0,0 +1,348 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_KSL_H
+#define NGHTTP3_KSL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdlib.h>
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_objalloc.h"
+
+/*
+ * Skip List using single key instead of range.
+ */
+
+#define NGHTTP3_KSL_DEGR 16
+/* NGHTTP3_KSL_MAX_NBLK is the maximum number of nodes which a single
+ block can contain. */
+#define NGHTTP3_KSL_MAX_NBLK (2 * NGHTTP3_KSL_DEGR - 1)
+/* NGHTTP3_KSL_MIN_NBLK is the minimum number of nodes which a single
+ block other than root must contains. */
+#define NGHTTP3_KSL_MIN_NBLK (NGHTTP3_KSL_DEGR - 1)
+
+/*
+ * nghttp3_ksl_key represents key in nghttp3_ksl.
+ */
+typedef void nghttp3_ksl_key;
+
+typedef struct nghttp3_ksl_node nghttp3_ksl_node;
+
+typedef struct nghttp3_ksl_blk nghttp3_ksl_blk;
+
+/*
+ * nghttp3_ksl_node is a node which contains either nghttp3_ksl_blk or
+ * opaque data. If a node is an internal node, it contains
+ * nghttp3_ksl_blk. Otherwise, it has data. The key is stored at the
+ * location starting at key.
+ */
+struct nghttp3_ksl_node {
+ union {
+ nghttp3_ksl_blk *blk;
+ void *data;
+ };
+ union {
+ uint64_t align;
+ /* key is a buffer to include key associated to this node.
+ Because the length of key is unknown until nghttp3_ksl_init is
+ called, the actual buffer will be allocated after this
+ field. */
+ uint8_t key[1];
+ };
+};
+
+/*
+ * nghttp3_ksl_blk contains nghttp3_ksl_node objects.
+ */
+struct nghttp3_ksl_blk {
+ union {
+ struct {
+ /* next points to the next block if leaf field is nonzero. */
+ nghttp3_ksl_blk *next;
+ /* prev points to the previous block if leaf field is
+ nonzero. */
+ nghttp3_ksl_blk *prev;
+ /* n is the number of nodes this object contains in nodes. */
+ uint32_t n;
+ /* leaf is nonzero if this block contains leaf nodes. */
+ uint32_t leaf;
+ union {
+ uint64_t align;
+ /* nodes is a buffer to contain NGHTTP3_KSL_MAX_NBLK
+ nghttp3_ksl_node objects. Because nghttp3_ksl_node object
+ is allocated along with the additional variable length key
+ storage, the size of buffer is unknown until
+ nghttp3_ksl_init is called. */
+ uint8_t nodes[1];
+ };
+ };
+
+ nghttp3_opl_entry oplent;
+ };
+};
+
+nghttp3_objalloc_def(ksl_blk, nghttp3_ksl_blk, oplent);
+
+/*
+ * nghttp3_ksl_compar is a function type which returns nonzero if key
+ * |lhs| should be placed before |rhs|. It returns 0 otherwise.
+ */
+typedef int (*nghttp3_ksl_compar)(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs);
+
+typedef struct nghttp3_ksl nghttp3_ksl;
+
+typedef struct nghttp3_ksl_it nghttp3_ksl_it;
+
+/*
+ * nghttp3_ksl_it is a forward iterator to iterate nodes.
+ */
+struct nghttp3_ksl_it {
+ const nghttp3_ksl *ksl;
+ nghttp3_ksl_blk *blk;
+ size_t i;
+};
+
+/*
+ * nghttp3_ksl is a deterministic paged skip list.
+ */
+struct nghttp3_ksl {
+ nghttp3_objalloc blkalloc;
+ /* head points to the root block. */
+ nghttp3_ksl_blk *head;
+ /* front points to the first leaf block. */
+ nghttp3_ksl_blk *front;
+ /* back points to the last leaf block. */
+ nghttp3_ksl_blk *back;
+ nghttp3_ksl_compar compar;
+ size_t n;
+ /* keylen is the size of key */
+ size_t keylen;
+ /* nodelen is the actual size of nghttp3_ksl_node including key
+ storage. */
+ size_t nodelen;
+};
+
+/*
+ * nghttp3_ksl_init initializes |ksl|. |compar| specifies compare
+ * function. |keylen| is the length of key.
+ */
+void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar,
+ size_t keylen, const nghttp3_mem *mem);
+
+/*
+ * nghttp3_ksl_free frees resources allocated for |ksl|. If |ksl| is
+ * NULL, this function does nothing. It does not free the memory
+ * region pointed by |ksl| itself.
+ */
+void nghttp3_ksl_free(nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_insert inserts |key| with its associated |data|. On
+ * successful insertion, the iterator points to the inserted node is
+ * stored in |*it|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_INVALID_ARGUMENT
+ * |key| already exists.
+ */
+int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_key *key, void *data);
+
+/*
+ * nghttp3_ksl_remove removes the |key| from |ksl|.
+ *
+ * This function assigns the iterator to |*it|, which points to the
+ * node which is located at the right next of the removed node if |it|
+ * is not NULL. If |key| is not found, no deletion takes place and
+ * the return value of nghttp3_ksl_end(ksl) is assigned to |*it|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_INVALID_ARGUMENT
+ * |key| does not exist.
+ */
+int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_key *key);
+
+/*
+ * nghttp3_ksl_remove_hint removes the |key| from |ksl|. |hint| must
+ * point to the same node denoted by |key|. |hint| is used to remove
+ * a node efficiently in some cases. Other than that, it behaves
+ * exactly like nghttp3_ksl_remove. |it| and |hint| can point to the
+ * same object.
+ */
+int nghttp3_ksl_remove_hint(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
+ const nghttp3_ksl_it *hint,
+ const nghttp3_ksl_key *key);
+
+/*
+ * nghttp3_ksl_lower_bound returns the iterator which points to the
+ * first node which has the key which is equal to |key| or the last
+ * node which satisfies !compar(&node->key, key). If there is no such
+ * node, it returns the iterator which satisfies nghttp3_ksl_it_end(it)
+ * != 0.
+ */
+nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl,
+ const nghttp3_ksl_key *key);
+
+/*
+ * nghttp3_ksl_lower_bound_compar works like nghttp3_ksl_lower_bound,
+ * but it takes custom function |compar| to do lower bound search.
+ */
+nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl,
+ const nghttp3_ksl_key *key,
+ nghttp3_ksl_compar compar);
+
+/*
+ * nghttp3_ksl_update_key replaces the key of nodes which has |old_key|
+ * with |new_key|. |new_key| must be strictly greater than the
+ * previous node and strictly smaller than the next node.
+ */
+void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key,
+ const nghttp3_ksl_key *new_key);
+
+/*
+ * nghttp3_ksl_begin returns the iterator which points to the first
+ * node. If there is no node in |ksl|, it returns the iterator which
+ * satisfies nghttp3_ksl_it_end(it) != 0.
+ */
+nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_end returns the iterator which points to the node
+ * following the last node. The returned object satisfies
+ * nghttp3_ksl_it_end(). If there is no node in |ksl|, it returns the
+ * iterator which satisfies nghttp3_ksl_it_begin(it) != 0.
+ */
+nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_len returns the number of elements stored in |ksl|.
+ */
+size_t nghttp3_ksl_len(nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_clear removes all elements stored in |ksl|.
+ */
+void nghttp3_ksl_clear(nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_nth_node returns the |n|th node under |blk|.
+ */
+#define nghttp3_ksl_nth_node(KSL, BLK, N) \
+ ((nghttp3_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N)))
+
+/*
+ * nghttp3_ksl_print prints its internal state in stderr. It assumes
+ * that the key is of type int64_t. This function should be used for
+ * the debugging purpose only.
+ */
+void nghttp3_ksl_print(nghttp3_ksl *ksl);
+
+/*
+ * nghttp3_ksl_it_init initializes |it|.
+ */
+void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl,
+ nghttp3_ksl_blk *blk, size_t i);
+
+/*
+ * nghttp3_ksl_it_get returns the data associated to the node which
+ * |it| points to. It is undefined to call this function when
+ * nghttp3_ksl_it_end(it) returns nonzero.
+ */
+#define nghttp3_ksl_it_get(IT) \
+ nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data
+
+/*
+ * nghttp3_ksl_it_next advances the iterator by one. It is undefined
+ * if this function is called when nghttp3_ksl_it_end(it) returns
+ * nonzero.
+ */
+#define nghttp3_ksl_it_next(IT) \
+ (++(IT)->i == (IT)->blk->n && (IT)->blk->next \
+ ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \
+ : 0)
+
+/*
+ * nghttp3_ksl_it_prev moves backward the iterator by one. It is
+ * undefined if this function is called when nghttp3_ksl_it_begin(it)
+ * returns nonzero.
+ */
+void nghttp3_ksl_it_prev(nghttp3_ksl_it *it);
+
+/*
+ * nghttp3_ksl_it_end returns nonzero if |it| points to the beyond the
+ * last node.
+ */
+#define nghttp3_ksl_it_end(IT) \
+ ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL)
+
+/*
+ * nghttp3_ksl_it_begin returns nonzero if |it| points to the first
+ * node. |it| might satisfy both nghttp3_ksl_it_begin(&it) and
+ * nghttp3_ksl_it_end(&it) if the skip list has no node.
+ */
+int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it);
+
+/*
+ * nghttp3_ksl_key returns the key of the node which |it| points to.
+ * It is undefined to call this function when nghttp3_ksl_it_end(it)
+ * returns nonzero.
+ */
+#define nghttp3_ksl_it_key(IT) \
+ ((nghttp3_ksl_key *)nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key)
+
+/*
+ * nghttp3_ksl_range_compar is an implementation of
+ * nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to
+ * nghttp3_range object and the function returns nonzero if (const
+ * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range
+ * *)(rhs->ptr)->begin.
+ */
+int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs);
+
+/*
+ * nghttp3_ksl_range_exclusive_compar is an implementation of
+ * nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to
+ * nghttp3_range object and the function returns nonzero if (const
+ * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range
+ * *)(rhs->ptr)->begin and the 2 ranges do not intersect.
+ */
+int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs);
+
+#endif /* NGHTTP3_KSL_H */
diff --git a/lib/nghttp3_macro.h b/lib/nghttp3_macro.h
new file mode 100644
index 0000000..a44e907
--- /dev/null
+++ b/lib/nghttp3_macro.h
@@ -0,0 +1,51 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_MACRO_H
+#define NGHTTP3_MACRO_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stddef.h>
+
+#include <nghttp3/nghttp3.h>
+
+#define nghttp3_min(A, B) ((A) < (B) ? (A) : (B))
+#define nghttp3_max(A, B) ((A) > (B) ? (A) : (B))
+
+#define nghttp3_struct_of(ptr, type, member) \
+ ((type *)(void *)((char *)(ptr)-offsetof(type, member)))
+
+#define nghttp3_arraylen(A) (sizeof(A) / sizeof(*(A)))
+
+#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0)
+
+/* NGHTTP3_MAX_VARINT` is the maximum value which can be encoded in
+ variable-length integer encoding. */
+#define NGHTTP3_MAX_VARINT ((1ULL << 62) - 1)
+
+#endif /* NGHTTP3_MACRO_H */
diff --git a/lib/nghttp3_map.c b/lib/nghttp3_map.c
new file mode 100644
index 0000000..fcfc31a
--- /dev/null
+++ b/lib/nghttp3_map.c
@@ -0,0 +1,337 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_map.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "nghttp3_conv.h"
+
+#define NGHTTP3_INITIAL_TABLE_LENBITS 4
+
+void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) {
+ map->mem = mem;
+ map->tablelen = 0;
+ map->tablelenbits = 0;
+ map->table = NULL;
+ map->size = 0;
+}
+
+void nghttp3_map_free(nghttp3_map *map) {
+ if (!map) {
+ return;
+ }
+
+ nghttp3_mem_free(map->mem, map->table);
+}
+
+void nghttp3_map_each_free(nghttp3_map *map, int (*func)(void *data, void *ptr),
+ void *ptr) {
+ uint32_t i;
+ nghttp3_map_bucket *bkt;
+
+ for (i = 0; i < map->tablelen; ++i) {
+ bkt = &map->table[i];
+
+ if (bkt->data == NULL) {
+ continue;
+ }
+
+ func(bkt->data, ptr);
+ }
+}
+
+int nghttp3_map_each(nghttp3_map *map, int (*func)(void *data, void *ptr),
+ void *ptr) {
+ int rv;
+ uint32_t i;
+ nghttp3_map_bucket *bkt;
+
+ if (map->size == 0) {
+ return 0;
+ }
+
+ for (i = 0; i < map->tablelen; ++i) {
+ bkt = &map->table[i];
+
+ if (bkt->data == NULL) {
+ continue;
+ }
+
+ rv = func(bkt->data, ptr);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+static uint32_t hash(nghttp3_map_key_type key) {
+ return (uint32_t)((key * 11400714819323198485llu) >> 32);
+}
+
+static size_t h2idx(uint32_t hash, uint32_t bits) {
+ return hash >> (32 - bits);
+}
+
+static size_t distance(uint32_t tablelen, uint32_t tablelenbits,
+ nghttp3_map_bucket *bkt, size_t idx) {
+ return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1);
+}
+
+static void map_bucket_swap(nghttp3_map_bucket *bkt, uint32_t *phash,
+ nghttp3_map_key_type *pkey, void **pdata) {
+ uint32_t h = bkt->hash;
+ nghttp3_map_key_type key = bkt->key;
+ void *data = bkt->data;
+
+ bkt->hash = *phash;
+ bkt->key = *pkey;
+ bkt->data = *pdata;
+
+ *phash = h;
+ *pkey = key;
+ *pdata = data;
+}
+
+static void map_bucket_set_data(nghttp3_map_bucket *bkt, uint32_t hash,
+ nghttp3_map_key_type key, void *data) {
+ bkt->hash = hash;
+ bkt->key = key;
+ bkt->data = data;
+}
+
+void nghttp3_map_print_distance(nghttp3_map *map) {
+ uint32_t i;
+ size_t idx;
+ nghttp3_map_bucket *bkt;
+
+ for (i = 0; i < map->tablelen; ++i) {
+ bkt = &map->table[i];
+
+ if (bkt->data == NULL) {
+ fprintf(stderr, "@%u <EMPTY>\n", i);
+ continue;
+ }
+
+ idx = h2idx(bkt->hash, map->tablelenbits);
+ fprintf(stderr, "@%u hash=%08x key=%" PRIu64 " base=%zu distance=%zu\n", i,
+ bkt->hash, bkt->key, idx,
+ distance(map->tablelen, map->tablelenbits, bkt, idx));
+ }
+}
+
+static int insert(nghttp3_map_bucket *table, uint32_t tablelen,
+ uint32_t tablelenbits, uint32_t hash,
+ nghttp3_map_key_type key, void *data) {
+ size_t idx = h2idx(hash, tablelenbits);
+ size_t d = 0, dd;
+ nghttp3_map_bucket *bkt;
+
+ for (;;) {
+ bkt = &table[idx];
+
+ if (bkt->data == NULL) {
+ map_bucket_set_data(bkt, hash, key, data);
+ return 0;
+ }
+
+ dd = distance(tablelen, tablelenbits, bkt, idx);
+ if (d > dd) {
+ map_bucket_swap(bkt, &hash, &key, &data);
+ d = dd;
+ } else if (bkt->key == key) {
+ /* TODO This check is just a waste after first swap or if this
+ function is called from map_resize. That said, there is no
+ difference with or without this conditional in performance
+ wise. */
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ ++d;
+ idx = (idx + 1) & (tablelen - 1);
+ }
+}
+
+/* new_tablelen must be power of 2 and new_tablelen == (1 <<
+ new_tablelenbits) must hold. */
+static int map_resize(nghttp3_map *map, uint32_t new_tablelen,
+ uint32_t new_tablelenbits) {
+ uint32_t i;
+ nghttp3_map_bucket *new_table;
+ nghttp3_map_bucket *bkt;
+ int rv;
+ (void)rv;
+
+ new_table =
+ nghttp3_mem_calloc(map->mem, new_tablelen, sizeof(nghttp3_map_bucket));
+ if (new_table == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ for (i = 0; i < map->tablelen; ++i) {
+ bkt = &map->table[i];
+ if (bkt->data == NULL) {
+ continue;
+ }
+ rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key,
+ bkt->data);
+
+ assert(0 == rv);
+ }
+
+ nghttp3_mem_free(map->mem, map->table);
+ map->tablelen = new_tablelen;
+ map->tablelenbits = new_tablelenbits;
+ map->table = new_table;
+
+ return 0;
+}
+
+int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data) {
+ int rv;
+
+ assert(data);
+
+ /* Load factor is 0.75 */
+ if ((map->size + 1) * 4 > map->tablelen * 3) {
+ if (map->tablelen) {
+ rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
+ if (rv != 0) {
+ return rv;
+ }
+ } else {
+ rv = map_resize(map, 1 << NGHTTP3_INITIAL_TABLE_LENBITS,
+ NGHTTP3_INITIAL_TABLE_LENBITS);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
+
+ rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key,
+ data);
+ if (rv != 0) {
+ return rv;
+ }
+ ++map->size;
+ return 0;
+}
+
+void *nghttp3_map_find(nghttp3_map *map, nghttp3_map_key_type key) {
+ uint32_t h;
+ size_t idx;
+ nghttp3_map_bucket *bkt;
+ size_t d = 0;
+
+ if (map->size == 0) {
+ return NULL;
+ }
+
+ h = hash(key);
+ idx = h2idx(h, map->tablelenbits);
+
+ for (;;) {
+ bkt = &map->table[idx];
+
+ if (bkt->data == NULL ||
+ d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
+ return NULL;
+ }
+
+ if (bkt->key == key) {
+ return bkt->data;
+ }
+
+ ++d;
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
+}
+
+int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key) {
+ uint32_t h;
+ size_t idx, didx;
+ nghttp3_map_bucket *bkt;
+ size_t d = 0;
+
+ if (map->size == 0) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ h = hash(key);
+ idx = h2idx(h, map->tablelenbits);
+
+ for (;;) {
+ bkt = &map->table[idx];
+
+ if (bkt->data == NULL ||
+ d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ if (bkt->key == key) {
+ map_bucket_set_data(bkt, 0, 0, NULL);
+
+ didx = idx;
+ idx = (idx + 1) & (map->tablelen - 1);
+
+ for (;;) {
+ bkt = &map->table[idx];
+ if (bkt->data == NULL ||
+ distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) {
+ break;
+ }
+
+ map->table[didx] = *bkt;
+ map_bucket_set_data(bkt, 0, 0, NULL);
+ didx = idx;
+
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
+
+ --map->size;
+
+ return 0;
+ }
+
+ ++d;
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
+}
+
+void nghttp3_map_clear(nghttp3_map *map) {
+ if (map->tablelen == 0) {
+ return;
+ }
+
+ memset(map->table, 0, sizeof(*map->table) * map->tablelen);
+ map->size = 0;
+}
+
+size_t nghttp3_map_size(nghttp3_map *map) { return map->size; }
diff --git a/lib/nghttp3_map.h b/lib/nghttp3_map.h
new file mode 100644
index 0000000..79dff02
--- /dev/null
+++ b/lib/nghttp3_map.h
@@ -0,0 +1,137 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_MAP_H
+#define NGHTTP3_MAP_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+
+/* Implementation of unordered map */
+
+typedef uint64_t nghttp3_map_key_type;
+
+typedef struct nghttp3_map_bucket {
+ uint32_t hash;
+ nghttp3_map_key_type key;
+ void *data;
+} nghttp3_map_bucket;
+
+typedef struct nghttp3_map {
+ nghttp3_map_bucket *table;
+ const nghttp3_mem *mem;
+ size_t size;
+ uint32_t tablelen;
+ uint32_t tablelenbits;
+} nghttp3_map;
+
+/*
+ * Initializes the map |map|.
+ */
+void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem);
+
+/*
+ * Deallocates any resources allocated for |map|. The stored entries
+ * are not freed by this function. Use nghttp3_map_each_free() to free
+ * each entries.
+ */
+void nghttp3_map_free(nghttp3_map *map);
+
+/*
+ * Deallocates each entries using |func| function and any resources
+ * allocated for |map|. The |func| function is responsible for freeing
+ * given the |data| object. The |ptr| will be passed to the |func| as
+ * send argument. The return value of the |func| will be ignored.
+ */
+void nghttp3_map_each_free(nghttp3_map *map, int (*func)(void *data, void *ptr),
+ void *ptr);
+
+/*
+ * Inserts the new |data| with the |key| to the map |map|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_INVALID_ARGUMENT
+ * The item associated by |key| already exists.
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory
+ */
+int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data);
+
+/*
+ * Returns the data associated by the key |key|. If there is no such
+ * data, this function returns NULL.
+ */
+void *nghttp3_map_find(nghttp3_map *map, nghttp3_map_key_type key);
+
+/*
+ * Removes the data associated by the key |key| from the |map|. The
+ * removed data is not freed by this function.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_INVALID_ARGUMENT
+ * The data associated by |key| does not exist.
+ */
+int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key);
+
+/*
+ * Removes all entries from |map|.
+ */
+void nghttp3_map_clear(nghttp3_map *map);
+
+/*
+ * Returns the number of items stored in the map |map|.
+ */
+size_t nghttp3_map_size(nghttp3_map *map);
+
+/*
+ * Applies the function |func| to each data in the |map| with the
+ * optional user supplied pointer |ptr|.
+ *
+ * If the |func| returns 0, this function calls the |func| with the
+ * next data. If the |func| returns nonzero, it will not call the
+ * |func| for further entries and return the return value of the
+ * |func| immediately. Thus, this function returns 0 if all the
+ * invocations of the |func| return 0, or nonzero value which the last
+ * invocation of |func| returns.
+ *
+ * Don't use this function to free each data. Use
+ * nghttp3_map_each_free() instead.
+ */
+int nghttp3_map_each(nghttp3_map *map, int (*func)(void *data, void *ptr),
+ void *ptr);
+
+void nghttp3_map_print_distance(nghttp3_map *map);
+
+#endif /* NGHTTP3_MAP_H */
diff --git a/lib/nghttp3_mem.c b/lib/nghttp3_mem.c
new file mode 100644
index 0000000..0379e99
--- /dev/null
+++ b/lib/nghttp3_mem.c
@@ -0,0 +1,124 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2014 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_mem.h"
+
+#include <stdio.h>
+
+static void *default_malloc(size_t size, void *user_data) {
+ (void)user_data;
+
+ return malloc(size);
+}
+
+static void default_free(void *ptr, void *user_data) {
+ (void)user_data;
+
+ free(ptr);
+}
+
+static void *default_calloc(size_t nmemb, size_t size, void *user_data) {
+ (void)user_data;
+
+ return calloc(nmemb, size);
+}
+
+static void *default_realloc(void *ptr, size_t size, void *user_data) {
+ (void)user_data;
+
+ return realloc(ptr, size);
+}
+
+static nghttp3_mem mem_default = {NULL, default_malloc, default_free,
+ default_calloc, default_realloc};
+
+const nghttp3_mem *nghttp3_mem_default(void) { return &mem_default; }
+
+#ifndef MEMDEBUG
+void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size) {
+ return mem->malloc(size, mem->user_data);
+}
+
+void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr) {
+ mem->free(ptr, mem->user_data);
+}
+
+void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size) {
+ return mem->calloc(nmemb, size, mem->user_data);
+}
+
+void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size) {
+ return mem->realloc(ptr, size, mem->user_data);
+}
+#else /* MEMDEBUG */
+void *nghttp3_mem_malloc_debug(const nghttp3_mem *mem, size_t size,
+ const char *func, const char *file,
+ size_t line) {
+ void *nptr = mem->malloc(size, mem->user_data);
+
+ fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func,
+ file, line);
+
+ return nptr;
+}
+
+void nghttp3_mem_free_debug(const nghttp3_mem *mem, void *ptr, const char *func,
+ const char *file, size_t line) {
+ fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line);
+
+ mem->free(ptr, mem->user_data);
+}
+
+void nghttp3_mem_free2_debug(const nghttp3_free free_func, void *ptr,
+ void *user_data, const char *func,
+ const char *file, size_t line) {
+ fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line);
+
+ free_func(ptr, user_data);
+}
+
+void *nghttp3_mem_calloc_debug(const nghttp3_mem *mem, size_t nmemb,
+ size_t size, const char *func, const char *file,
+ size_t line) {
+ void *nptr = mem->calloc(nmemb, size, mem->user_data);
+
+ fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb,
+ size, func, file, line);
+
+ return nptr;
+}
+
+void *nghttp3_mem_realloc_debug(const nghttp3_mem *mem, void *ptr, size_t size,
+ const char *func, const char *file,
+ size_t line) {
+ void *nptr = mem->realloc(ptr, size, mem->user_data);
+
+ fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr,
+ size, func, file, line);
+
+ return nptr;
+}
+#endif /* MEMDEBUG */
diff --git a/lib/nghttp3_mem.h b/lib/nghttp3_mem.h
new file mode 100644
index 0000000..d6c3ada
--- /dev/null
+++ b/lib/nghttp3_mem.h
@@ -0,0 +1,80 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2014 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_MEM_H
+#define NGHTTP3_MEM_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+/* Convenient wrapper functions to call allocator function in
+ |mem|. */
+#ifndef MEMDEBUG
+void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size);
+void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr);
+void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size);
+void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size);
+#else /* MEMDEBUG */
+void *nghttp3_mem_malloc_debug(const nghttp3_mem *mem, size_t size,
+ const char *func, const char *file, size_t line);
+
+# define nghttp3_mem_malloc(MEM, SIZE) \
+ nghttp3_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__)
+
+void nghttp3_mem_free_debug(const nghttp3_mem *mem, void *ptr, const char *func,
+ const char *file, size_t line);
+
+# define nghttp3_mem_free(MEM, PTR) \
+ nghttp3_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__)
+
+void nghttp3_mem_free2_debug(nghttp3_free free_func, void *ptr, void *user_data,
+ const char *func, const char *file, size_t line);
+
+# define nghttp3_mem_free2(FREE_FUNC, PTR, USER_DATA) \
+ nghttp3_mem_free2_debug((FREE_FUNC), (PTR), (USER_DATA), __func__, \
+ __FILE__, __LINE__)
+
+void *nghttp3_mem_calloc_debug(const nghttp3_mem *mem, size_t nmemb,
+ size_t size, const char *func, const char *file,
+ size_t line);
+
+# define nghttp3_mem_calloc(MEM, NMEMB, SIZE) \
+ nghttp3_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__, \
+ __LINE__)
+
+void *nghttp3_mem_realloc_debug(const nghttp3_mem *mem, void *ptr, size_t size,
+ const char *func, const char *file,
+ size_t line);
+
+# define nghttp3_mem_realloc(MEM, PTR, SIZE) \
+ nghttp3_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__, \
+ __LINE__)
+#endif /* MEMDEBUG */
+
+#endif /* NGHTTP3_MEM_H */
diff --git a/lib/nghttp3_objalloc.c b/lib/nghttp3_objalloc.c
new file mode 100644
index 0000000..0c97860
--- /dev/null
+++ b/lib/nghttp3_objalloc.c
@@ -0,0 +1,41 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_objalloc.h"
+
+void nghttp3_objalloc_init(nghttp3_objalloc *objalloc, size_t blklen,
+ const nghttp3_mem *mem) {
+ nghttp3_balloc_init(&objalloc->balloc, blklen, mem);
+ nghttp3_opl_init(&objalloc->opl);
+}
+
+void nghttp3_objalloc_free(nghttp3_objalloc *objalloc) {
+ nghttp3_balloc_free(&objalloc->balloc);
+}
+
+void nghttp3_objalloc_clear(nghttp3_objalloc *objalloc) {
+ nghttp3_opl_clear(&objalloc->opl);
+ nghttp3_balloc_clear(&objalloc->balloc);
+}
diff --git a/lib/nghttp3_objalloc.h b/lib/nghttp3_objalloc.h
new file mode 100644
index 0000000..da39447
--- /dev/null
+++ b/lib/nghttp3_objalloc.h
@@ -0,0 +1,141 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_OBJALLOC_H
+#define NGHTTP3_OBJALLOC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_balloc.h"
+#include "nghttp3_opl.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_mem.h"
+
+/*
+ * nghttp3_objalloc combines nghttp3_balloc and nghttp3_opl, and
+ * provides an object pool with the custom allocator to reduce the
+ * allocation and deallocation overheads for small objects.
+ */
+typedef struct nghttp3_objalloc {
+ nghttp3_balloc balloc;
+ nghttp3_opl opl;
+} nghttp3_objalloc;
+
+/*
+ * nghttp3_objalloc_init initializes |objalloc|. |blklen| is directly
+ * passed to nghttp3_balloc_init.
+ */
+void nghttp3_objalloc_init(nghttp3_objalloc *objalloc, size_t blklen,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_objalloc_free releases all allocated resources.
+ */
+void nghttp3_objalloc_free(nghttp3_objalloc *objalloc);
+
+/*
+ * nghttp3_objalloc_clear releases all allocated resources and
+ * initializes its state.
+ */
+void nghttp3_objalloc_clear(nghttp3_objalloc *objalloc);
+
+#ifndef NOMEMPOOL
+# define nghttp3_objalloc_def(NAME, TYPE, OPLENTFIELD) \
+ inline static void nghttp3_objalloc_##NAME##_init( \
+ nghttp3_objalloc *objalloc, size_t nmemb, const nghttp3_mem *mem) { \
+ nghttp3_objalloc_init( \
+ objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
+ } \
+ \
+ inline static TYPE *nghttp3_objalloc_##NAME##_get( \
+ nghttp3_objalloc *objalloc) { \
+ nghttp3_opl_entry *oplent = nghttp3_opl_pop(&objalloc->opl); \
+ TYPE *obj; \
+ int rv; \
+ \
+ if (!oplent) { \
+ rv = nghttp3_balloc_get(&objalloc->balloc, (void **)&obj, \
+ sizeof(TYPE)); \
+ if (rv != 0) { \
+ return NULL; \
+ } \
+ \
+ return obj; \
+ } \
+ \
+ return nghttp3_struct_of(oplent, TYPE, OPLENTFIELD); \
+ } \
+ \
+ inline static TYPE *nghttp3_objalloc_##NAME##_len_get( \
+ nghttp3_objalloc *objalloc, size_t len) { \
+ nghttp3_opl_entry *oplent = nghttp3_opl_pop(&objalloc->opl); \
+ TYPE *obj; \
+ int rv; \
+ \
+ if (!oplent) { \
+ rv = nghttp3_balloc_get(&objalloc->balloc, (void **)&obj, len); \
+ if (rv != 0) { \
+ return NULL; \
+ } \
+ \
+ return obj; \
+ } \
+ \
+ return nghttp3_struct_of(oplent, TYPE, OPLENTFIELD); \
+ } \
+ \
+ inline static void nghttp3_objalloc_##NAME##_release( \
+ nghttp3_objalloc *objalloc, TYPE *obj) { \
+ nghttp3_opl_push(&objalloc->opl, &obj->OPLENTFIELD); \
+ }
+#else /* NOMEMPOOL */
+# define nghttp3_objalloc_def(NAME, TYPE, OPLENTFIELD) \
+ inline static void nghttp3_objalloc_##NAME##_init( \
+ nghttp3_objalloc *objalloc, size_t nmemb, const nghttp3_mem *mem) { \
+ nghttp3_objalloc_init( \
+ objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
+ } \
+ \
+ inline static TYPE *nghttp3_objalloc_##NAME##_get( \
+ nghttp3_objalloc *objalloc) { \
+ return nghttp3_mem_malloc(objalloc->balloc.mem, sizeof(TYPE)); \
+ } \
+ \
+ inline static TYPE *nghttp3_objalloc_##NAME##_len_get( \
+ nghttp3_objalloc *objalloc, size_t len) { \
+ return nghttp3_mem_malloc(objalloc->balloc.mem, len); \
+ } \
+ \
+ inline static void nghttp3_objalloc_##NAME##_release( \
+ nghttp3_objalloc *objalloc, TYPE *obj) { \
+ nghttp3_mem_free(objalloc->balloc.mem, obj); \
+ }
+#endif /* NOMEMPOOL */
+
+#endif /* NGHTTP3_OBJALLOC_H */
diff --git a/lib/nghttp3_opl.c b/lib/nghttp3_opl.c
new file mode 100644
index 0000000..eb8ebdd
--- /dev/null
+++ b/lib/nghttp3_opl.c
@@ -0,0 +1,47 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_opl.h"
+
+void nghttp3_opl_init(nghttp3_opl *opl) { opl->head = NULL; }
+
+void nghttp3_opl_push(nghttp3_opl *opl, nghttp3_opl_entry *ent) {
+ ent->next = opl->head;
+ opl->head = ent;
+}
+
+nghttp3_opl_entry *nghttp3_opl_pop(nghttp3_opl *opl) {
+ nghttp3_opl_entry *ent = opl->head;
+
+ if (!ent) {
+ return NULL;
+ }
+
+ opl->head = ent->next;
+
+ return ent;
+}
+
+void nghttp3_opl_clear(nghttp3_opl *opl) { opl->head = NULL; }
diff --git a/lib/nghttp3_opl.h b/lib/nghttp3_opl.h
new file mode 100644
index 0000000..8c8a4f2
--- /dev/null
+++ b/lib/nghttp3_opl.h
@@ -0,0 +1,66 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_OPL_H
+#define NGHTTP3_OPL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+typedef struct nghttp3_opl_entry nghttp3_opl_entry;
+
+struct nghttp3_opl_entry {
+ nghttp3_opl_entry *next;
+};
+
+/*
+ * nghttp3_opl is an object memory pool.
+ */
+typedef struct nghttp3_opl {
+ nghttp3_opl_entry *head;
+} nghttp3_opl;
+
+/*
+ * nghttp3_opl_init initializes |opl|.
+ */
+void nghttp3_opl_init(nghttp3_opl *opl);
+
+/*
+ * nghttp3_opl_push inserts |ent| to |opl| head.
+ */
+void nghttp3_opl_push(nghttp3_opl *opl, nghttp3_opl_entry *ent);
+
+/*
+ * nghttp3_opl_pop removes the first nghttp3_opl_entry from |opl| and
+ * returns it. If |opl| does not have any entry, it returns NULL.
+ */
+nghttp3_opl_entry *nghttp3_opl_pop(nghttp3_opl *opl);
+
+void nghttp3_opl_clear(nghttp3_opl *opl);
+
+#endif /* NGHTTP3_OPL_H */
diff --git a/lib/nghttp3_pq.c b/lib/nghttp3_pq.c
new file mode 100644
index 0000000..5d09050
--- /dev/null
+++ b/lib/nghttp3_pq.c
@@ -0,0 +1,168 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_pq.h"
+
+#include <assert.h>
+
+#include "nghttp3_macro.h"
+
+void nghttp3_pq_init(nghttp3_pq *pq, nghttp3_less less,
+ const nghttp3_mem *mem) {
+ pq->mem = mem;
+ pq->capacity = 0;
+ pq->q = NULL;
+ pq->length = 0;
+ pq->less = less;
+}
+
+void nghttp3_pq_free(nghttp3_pq *pq) {
+ nghttp3_mem_free(pq->mem, pq->q);
+ pq->q = NULL;
+}
+
+static void swap(nghttp3_pq *pq, size_t i, size_t j) {
+ nghttp3_pq_entry *a = pq->q[i];
+ nghttp3_pq_entry *b = pq->q[j];
+
+ pq->q[i] = b;
+ b->index = i;
+ pq->q[j] = a;
+ a->index = j;
+}
+
+static void bubble_up(nghttp3_pq *pq, size_t index) {
+ size_t parent;
+ while (index != 0) {
+ parent = (index - 1) / 2;
+ if (!pq->less(pq->q[index], pq->q[parent])) {
+ return;
+ }
+ swap(pq, parent, index);
+ index = parent;
+ }
+}
+
+int nghttp3_pq_push(nghttp3_pq *pq, nghttp3_pq_entry *item) {
+ if (pq->capacity <= pq->length) {
+ void *nq;
+ size_t ncapacity;
+
+ ncapacity = nghttp3_max(4, (pq->capacity * 2));
+
+ nq = nghttp3_mem_realloc(pq->mem, pq->q,
+ ncapacity * sizeof(nghttp3_pq_entry *));
+ if (nq == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+ pq->capacity = ncapacity;
+ pq->q = nq;
+ }
+ pq->q[pq->length] = item;
+ item->index = pq->length;
+ ++pq->length;
+ bubble_up(pq, pq->length - 1);
+ return 0;
+}
+
+nghttp3_pq_entry *nghttp3_pq_top(const nghttp3_pq *pq) {
+ assert(pq->length);
+ return pq->q[0];
+}
+
+static void bubble_down(nghttp3_pq *pq, size_t index) {
+ size_t i, j, minindex;
+ for (;;) {
+ j = index * 2 + 1;
+ minindex = index;
+ for (i = 0; i < 2; ++i, ++j) {
+ if (j >= pq->length) {
+ break;
+ }
+ if (pq->less(pq->q[j], pq->q[minindex])) {
+ minindex = j;
+ }
+ }
+ if (minindex == index) {
+ return;
+ }
+ swap(pq, index, minindex);
+ index = minindex;
+ }
+}
+
+void nghttp3_pq_pop(nghttp3_pq *pq) {
+ if (pq->length > 0) {
+ pq->q[0] = pq->q[pq->length - 1];
+ pq->q[0]->index = 0;
+ --pq->length;
+ bubble_down(pq, 0);
+ }
+}
+
+void nghttp3_pq_remove(nghttp3_pq *pq, nghttp3_pq_entry *item) {
+ assert(pq->q[item->index] == item);
+
+ if (item->index == 0) {
+ nghttp3_pq_pop(pq);
+ return;
+ }
+
+ if (item->index == pq->length - 1) {
+ --pq->length;
+ return;
+ }
+
+ pq->q[item->index] = pq->q[pq->length - 1];
+ pq->q[item->index]->index = item->index;
+ --pq->length;
+
+ if (pq->less(item, pq->q[item->index])) {
+ bubble_down(pq, item->index);
+ } else {
+ bubble_up(pq, item->index);
+ }
+}
+
+int nghttp3_pq_empty(const nghttp3_pq *pq) { return pq->length == 0; }
+
+size_t nghttp3_pq_size(const nghttp3_pq *pq) { return pq->length; }
+
+int nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_pq_item_cb fun, void *arg) {
+ size_t i;
+
+ if (pq->length == 0) {
+ return 0;
+ }
+ for (i = 0; i < pq->length; ++i) {
+ if ((*fun)(pq->q[i], arg)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void nghttp3_pq_clear(nghttp3_pq *pq) { pq->length = 0; }
diff --git a/lib/nghttp3_pq.h b/lib/nghttp3_pq.h
new file mode 100644
index 0000000..c1a54f5
--- /dev/null
+++ b/lib/nghttp3_pq.h
@@ -0,0 +1,129 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_PQ_H
+#define NGHTTP3_PQ_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+
+/* Implementation of priority queue */
+
+/* NGHTTP3_PQ_BAD_INDEX is the priority queue index which indicates
+ that an entry is not queued. Assigning this value to
+ nghttp3_pq_entry.index can check that the entry is queued or not. */
+#define NGHTTP3_PQ_BAD_INDEX SIZE_MAX
+
+typedef struct nghttp3_pq_entry {
+ size_t index;
+} nghttp3_pq_entry;
+
+/* "less" function, return nonzero if |lhs| is less than |rhs|. */
+typedef int (*nghttp3_less)(const nghttp3_pq_entry *lhs,
+ const nghttp3_pq_entry *rhs);
+
+typedef struct nghttp3_pq {
+ /* The pointer to the pointer to the item stored */
+ nghttp3_pq_entry **q;
+ /* Memory allocator */
+ const nghttp3_mem *mem;
+ /* The number of items stored */
+ size_t length;
+ /* The maximum number of items this pq can store. This is
+ automatically extended when length is reached to this value. */
+ size_t capacity;
+ /* The less function between items */
+ nghttp3_less less;
+} nghttp3_pq;
+
+/*
+ * Initializes priority queue |pq| with compare function |cmp|.
+ */
+void nghttp3_pq_init(nghttp3_pq *pq, nghttp3_less less, const nghttp3_mem *mem);
+
+/*
+ * Deallocates any resources allocated for |pq|. The stored items are
+ * not freed by this function.
+ */
+void nghttp3_pq_free(nghttp3_pq *pq);
+
+/*
+ * Adds |item| to the priority queue |pq|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_pq_push(nghttp3_pq *pq, nghttp3_pq_entry *item);
+
+/*
+ * Returns item at the top of the queue |pq|. It is undefined if the
+ * queue is empty.
+ */
+nghttp3_pq_entry *nghttp3_pq_top(const nghttp3_pq *pq);
+
+/*
+ * Pops item at the top of the queue |pq|. The popped item is not
+ * freed by this function.
+ */
+void nghttp3_pq_pop(nghttp3_pq *pq);
+
+/*
+ * Returns nonzero if the queue |pq| is empty.
+ */
+int nghttp3_pq_empty(const nghttp3_pq *pq);
+
+/*
+ * Returns the number of items in the queue |pq|.
+ */
+size_t nghttp3_pq_size(const nghttp3_pq *pq);
+
+typedef int (*nghttp3_pq_item_cb)(nghttp3_pq_entry *item, void *arg);
+
+/*
+ * Applies |fun| to each item in |pq|. The |arg| is passed as arg
+ * parameter to callback function. This function must not change the
+ * ordering key. If the return value from callback is nonzero, this
+ * function returns 1 immediately without iterating remaining items.
+ * Otherwise this function returns 0.
+ */
+int nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_pq_item_cb fun, void *arg);
+
+/*
+ * Removes |item| from priority queue.
+ */
+void nghttp3_pq_remove(nghttp3_pq *pq, nghttp3_pq_entry *item);
+
+void nghttp3_pq_clear(nghttp3_pq *pq);
+
+#endif /* NGHTTP3_PQ_H */
diff --git a/lib/nghttp3_qpack.c b/lib/nghttp3_qpack.c
new file mode 100644
index 0000000..fece8f1
--- /dev/null
+++ b/lib/nghttp3_qpack.c
@@ -0,0 +1,4093 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_qpack.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "nghttp3_str.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_debug.h"
+#include "nghttp3_unreachable.h"
+
+/* NGHTTP3_QPACK_MAX_QPACK_STREAMS is the maximum number of concurrent
+ nghttp3_qpack_stream object to handle a client which never cancel
+ or acknowledge header block. After this limit, encoder stops using
+ dynamic table. */
+#define NGHTTP3_QPACK_MAX_QPACK_STREAMS 2000
+
+/* Make scalar initialization form of nghttp3_qpack_static_entry */
+#define MAKE_STATIC_ENT(I, T, H) \
+ { I, T, H }
+
+/* Generated by mkstatichdtbl.py */
+static nghttp3_qpack_static_entry token_stable[] = {
+ MAKE_STATIC_ENT(0, NGHTTP3_QPACK_TOKEN__AUTHORITY, 3153725150u),
+ MAKE_STATIC_ENT(15, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(16, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(17, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(18, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(19, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(20, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(21, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u),
+ MAKE_STATIC_ENT(1, NGHTTP3_QPACK_TOKEN__PATH, 3292848686u),
+ MAKE_STATIC_ENT(22, NGHTTP3_QPACK_TOKEN__SCHEME, 2510477674u),
+ MAKE_STATIC_ENT(23, NGHTTP3_QPACK_TOKEN__SCHEME, 2510477674u),
+ MAKE_STATIC_ENT(24, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(25, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(26, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(27, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(28, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(63, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(64, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(65, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(66, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(67, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(68, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(69, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(70, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(71, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u),
+ MAKE_STATIC_ENT(29, NGHTTP3_QPACK_TOKEN_ACCEPT, 136609321u),
+ MAKE_STATIC_ENT(30, NGHTTP3_QPACK_TOKEN_ACCEPT, 136609321u),
+ MAKE_STATIC_ENT(31, NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING, 3379649177u),
+ MAKE_STATIC_ENT(72, NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE, 1979086614u),
+ MAKE_STATIC_ENT(32, NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES, 1713753958u),
+ MAKE_STATIC_ENT(73, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS,
+ 901040780u),
+ MAKE_STATIC_ENT(74, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS,
+ 901040780u),
+ MAKE_STATIC_ENT(33, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS,
+ 1524311232u),
+ MAKE_STATIC_ENT(34, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS,
+ 1524311232u),
+ MAKE_STATIC_ENT(75, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS,
+ 1524311232u),
+ MAKE_STATIC_ENT(76, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS,
+ 2175229868u),
+ MAKE_STATIC_ENT(77, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS,
+ 2175229868u),
+ MAKE_STATIC_ENT(78, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS,
+ 2175229868u),
+ MAKE_STATIC_ENT(35, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN,
+ 2710797292u),
+ MAKE_STATIC_ENT(79, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS,
+ 2449824425u),
+ MAKE_STATIC_ENT(80, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS,
+ 3599549072u),
+ MAKE_STATIC_ENT(81, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD,
+ 2417078055u),
+ MAKE_STATIC_ENT(82, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD,
+ 2417078055u),
+ MAKE_STATIC_ENT(2, NGHTTP3_QPACK_TOKEN_AGE, 742476188u),
+ MAKE_STATIC_ENT(83, NGHTTP3_QPACK_TOKEN_ALT_SVC, 2148877059u),
+ MAKE_STATIC_ENT(84, NGHTTP3_QPACK_TOKEN_AUTHORIZATION, 2436257726u),
+ MAKE_STATIC_ENT(36, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(37, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(38, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(39, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(40, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(41, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u),
+ MAKE_STATIC_ENT(3, NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION, 3889184348u),
+ MAKE_STATIC_ENT(42, NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING, 65203592u),
+ MAKE_STATIC_ENT(43, NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING, 65203592u),
+ MAKE_STATIC_ENT(4, NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH, 1308181789u),
+ MAKE_STATIC_ENT(85, NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY,
+ 1569039836u),
+ MAKE_STATIC_ENT(44, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(45, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(46, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(47, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(48, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(49, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(50, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(51, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(52, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(53, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(54, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u),
+ MAKE_STATIC_ENT(5, NGHTTP3_QPACK_TOKEN_COOKIE, 2007449791u),
+ MAKE_STATIC_ENT(6, NGHTTP3_QPACK_TOKEN_DATE, 3564297305u),
+ MAKE_STATIC_ENT(86, NGHTTP3_QPACK_TOKEN_EARLY_DATA, 4080895051u),
+ MAKE_STATIC_ENT(7, NGHTTP3_QPACK_TOKEN_ETAG, 113792960u),
+ MAKE_STATIC_ENT(87, NGHTTP3_QPACK_TOKEN_EXPECT_CT, 1183214960u),
+ MAKE_STATIC_ENT(88, NGHTTP3_QPACK_TOKEN_FORWARDED, 1485178027u),
+ MAKE_STATIC_ENT(8, NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE, 2213050793u),
+ MAKE_STATIC_ENT(9, NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH, 2536202615u),
+ MAKE_STATIC_ENT(89, NGHTTP3_QPACK_TOKEN_IF_RANGE, 2340978238u),
+ MAKE_STATIC_ENT(10, NGHTTP3_QPACK_TOKEN_LAST_MODIFIED, 3226950251u),
+ MAKE_STATIC_ENT(11, NGHTTP3_QPACK_TOKEN_LINK, 232457833u),
+ MAKE_STATIC_ENT(12, NGHTTP3_QPACK_TOKEN_LOCATION, 200649126u),
+ MAKE_STATIC_ENT(90, NGHTTP3_QPACK_TOKEN_ORIGIN, 3649018447u),
+ MAKE_STATIC_ENT(91, NGHTTP3_QPACK_TOKEN_PURPOSE, 4212263681u),
+ MAKE_STATIC_ENT(55, NGHTTP3_QPACK_TOKEN_RANGE, 4208725202u),
+ MAKE_STATIC_ENT(13, NGHTTP3_QPACK_TOKEN_REFERER, 3969579366u),
+ MAKE_STATIC_ENT(92, NGHTTP3_QPACK_TOKEN_SERVER, 1085029842u),
+ MAKE_STATIC_ENT(14, NGHTTP3_QPACK_TOKEN_SET_COOKIE, 1848371000u),
+ MAKE_STATIC_ENT(56, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY,
+ 4138147361u),
+ MAKE_STATIC_ENT(57, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY,
+ 4138147361u),
+ MAKE_STATIC_ENT(58, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY,
+ 4138147361u),
+ MAKE_STATIC_ENT(93, NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN, 2432297564u),
+ MAKE_STATIC_ENT(94, NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS,
+ 2479169413u),
+ MAKE_STATIC_ENT(95, NGHTTP3_QPACK_TOKEN_USER_AGENT, 606444526u),
+ MAKE_STATIC_ENT(59, NGHTTP3_QPACK_TOKEN_VARY, 1085005381u),
+ MAKE_STATIC_ENT(60, NGHTTP3_QPACK_TOKEN_VARY, 1085005381u),
+ MAKE_STATIC_ENT(61, NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS,
+ 3644557769u),
+ MAKE_STATIC_ENT(96, NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR, 2914187656u),
+ MAKE_STATIC_ENT(97, NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS, 3993834824u),
+ MAKE_STATIC_ENT(98, NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS, 3993834824u),
+ MAKE_STATIC_ENT(62, NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION, 2501058888u),
+};
+
+/* Make scalar initialization form of nghttp3_qpack_static_entry */
+#define MAKE_STATIC_HD(N, V, T) \
+ { \
+ {NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \
+ {NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, T \
+ }
+
+static nghttp3_qpack_static_header stable[] = {
+ MAKE_STATIC_HD(":authority", "", NGHTTP3_QPACK_TOKEN__AUTHORITY),
+ MAKE_STATIC_HD(":path", "/", NGHTTP3_QPACK_TOKEN__PATH),
+ MAKE_STATIC_HD("age", "0", NGHTTP3_QPACK_TOKEN_AGE),
+ MAKE_STATIC_HD("content-disposition", "",
+ NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION),
+ MAKE_STATIC_HD("content-length", "0", NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH),
+ MAKE_STATIC_HD("cookie", "", NGHTTP3_QPACK_TOKEN_COOKIE),
+ MAKE_STATIC_HD("date", "", NGHTTP3_QPACK_TOKEN_DATE),
+ MAKE_STATIC_HD("etag", "", NGHTTP3_QPACK_TOKEN_ETAG),
+ MAKE_STATIC_HD("if-modified-since", "",
+ NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE),
+ MAKE_STATIC_HD("if-none-match", "", NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH),
+ MAKE_STATIC_HD("last-modified", "", NGHTTP3_QPACK_TOKEN_LAST_MODIFIED),
+ MAKE_STATIC_HD("link", "", NGHTTP3_QPACK_TOKEN_LINK),
+ MAKE_STATIC_HD("location", "", NGHTTP3_QPACK_TOKEN_LOCATION),
+ MAKE_STATIC_HD("referer", "", NGHTTP3_QPACK_TOKEN_REFERER),
+ MAKE_STATIC_HD("set-cookie", "", NGHTTP3_QPACK_TOKEN_SET_COOKIE),
+ MAKE_STATIC_HD(":method", "CONNECT", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "DELETE", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "GET", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "HEAD", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "OPTIONS", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "POST", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":method", "PUT", NGHTTP3_QPACK_TOKEN__METHOD),
+ MAKE_STATIC_HD(":scheme", "http", NGHTTP3_QPACK_TOKEN__SCHEME),
+ MAKE_STATIC_HD(":scheme", "https", NGHTTP3_QPACK_TOKEN__SCHEME),
+ MAKE_STATIC_HD(":status", "103", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "200", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "304", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "404", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "503", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD("accept", "*/*", NGHTTP3_QPACK_TOKEN_ACCEPT),
+ MAKE_STATIC_HD("accept", "application/dns-message",
+ NGHTTP3_QPACK_TOKEN_ACCEPT),
+ MAKE_STATIC_HD("accept-encoding", "gzip, deflate, br",
+ NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING),
+ MAKE_STATIC_HD("accept-ranges", "bytes", NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES),
+ MAKE_STATIC_HD("access-control-allow-headers", "cache-control",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS),
+ MAKE_STATIC_HD("access-control-allow-headers", "content-type",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS),
+ MAKE_STATIC_HD("access-control-allow-origin", "*",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN),
+ MAKE_STATIC_HD("cache-control", "max-age=0",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "max-age=2592000",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "max-age=604800",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "no-cache",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "no-store",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("cache-control", "public, max-age=31536000",
+ NGHTTP3_QPACK_TOKEN_CACHE_CONTROL),
+ MAKE_STATIC_HD("content-encoding", "br",
+ NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING),
+ MAKE_STATIC_HD("content-encoding", "gzip",
+ NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING),
+ MAKE_STATIC_HD("content-type", "application/dns-message",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "application/javascript",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "application/json",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "application/x-www-form-urlencoded",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "image/gif",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "image/jpeg",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "image/png",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "text/css",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "text/html; charset=utf-8",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "text/plain",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("content-type", "text/plain;charset=utf-8",
+ NGHTTP3_QPACK_TOKEN_CONTENT_TYPE),
+ MAKE_STATIC_HD("range", "bytes=0-", NGHTTP3_QPACK_TOKEN_RANGE),
+ MAKE_STATIC_HD("strict-transport-security", "max-age=31536000",
+ NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY),
+ MAKE_STATIC_HD("strict-transport-security",
+ "max-age=31536000; includesubdomains",
+ NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY),
+ MAKE_STATIC_HD("strict-transport-security",
+ "max-age=31536000; includesubdomains; preload",
+ NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY),
+ MAKE_STATIC_HD("vary", "accept-encoding", NGHTTP3_QPACK_TOKEN_VARY),
+ MAKE_STATIC_HD("vary", "origin", NGHTTP3_QPACK_TOKEN_VARY),
+ MAKE_STATIC_HD("x-content-type-options", "nosniff",
+ NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS),
+ MAKE_STATIC_HD("x-xss-protection", "1; mode=block",
+ NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION),
+ MAKE_STATIC_HD(":status", "100", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "204", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "206", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "302", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "400", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "403", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "421", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "425", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD(":status", "500", NGHTTP3_QPACK_TOKEN__STATUS),
+ MAKE_STATIC_HD("accept-language", "", NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE),
+ MAKE_STATIC_HD("access-control-allow-credentials", "FALSE",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS),
+ MAKE_STATIC_HD("access-control-allow-credentials", "TRUE",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS),
+ MAKE_STATIC_HD("access-control-allow-headers", "*",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS),
+ MAKE_STATIC_HD("access-control-allow-methods", "get",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS),
+ MAKE_STATIC_HD("access-control-allow-methods", "get, post, options",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS),
+ MAKE_STATIC_HD("access-control-allow-methods", "options",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS),
+ MAKE_STATIC_HD("access-control-expose-headers", "content-length",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS),
+ MAKE_STATIC_HD("access-control-request-headers", "content-type",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS),
+ MAKE_STATIC_HD("access-control-request-method", "get",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD),
+ MAKE_STATIC_HD("access-control-request-method", "post",
+ NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD),
+ MAKE_STATIC_HD("alt-svc", "clear", NGHTTP3_QPACK_TOKEN_ALT_SVC),
+ MAKE_STATIC_HD("authorization", "", NGHTTP3_QPACK_TOKEN_AUTHORIZATION),
+ MAKE_STATIC_HD("content-security-policy",
+ "script-src 'none'; object-src 'none'; base-uri 'none'",
+ NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY),
+ MAKE_STATIC_HD("early-data", "1", NGHTTP3_QPACK_TOKEN_EARLY_DATA),
+ MAKE_STATIC_HD("expect-ct", "", NGHTTP3_QPACK_TOKEN_EXPECT_CT),
+ MAKE_STATIC_HD("forwarded", "", NGHTTP3_QPACK_TOKEN_FORWARDED),
+ MAKE_STATIC_HD("if-range", "", NGHTTP3_QPACK_TOKEN_IF_RANGE),
+ MAKE_STATIC_HD("origin", "", NGHTTP3_QPACK_TOKEN_ORIGIN),
+ MAKE_STATIC_HD("purpose", "prefetch", NGHTTP3_QPACK_TOKEN_PURPOSE),
+ MAKE_STATIC_HD("server", "", NGHTTP3_QPACK_TOKEN_SERVER),
+ MAKE_STATIC_HD("timing-allow-origin", "*",
+ NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN),
+ MAKE_STATIC_HD("upgrade-insecure-requests", "1",
+ NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS),
+ MAKE_STATIC_HD("user-agent", "", NGHTTP3_QPACK_TOKEN_USER_AGENT),
+ MAKE_STATIC_HD("x-forwarded-for", "", NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR),
+ MAKE_STATIC_HD("x-frame-options", "deny",
+ NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS),
+ MAKE_STATIC_HD("x-frame-options", "sameorigin",
+ NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS),
+};
+
+static int memeq(const void *s1, const void *s2, size_t n) {
+ return n == 0 || memcmp(s1, s2, n) == 0;
+}
+
+/* Generated by genlibtokenlookup.py */
+static int32_t qpack_lookup_token(const uint8_t *name, size_t namelen) {
+ switch (namelen) {
+ case 2:
+ switch (name[1]) {
+ case 'e':
+ if (memeq("t", name, 1)) {
+ return NGHTTP3_QPACK_TOKEN_TE;
+ }
+ break;
+ }
+ break;
+ case 3:
+ switch (name[2]) {
+ case 'e':
+ if (memeq("ag", name, 2)) {
+ return NGHTTP3_QPACK_TOKEN_AGE;
+ }
+ break;
+ }
+ break;
+ case 4:
+ switch (name[3]) {
+ case 'e':
+ if (memeq("dat", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_DATE;
+ }
+ break;
+ case 'g':
+ if (memeq("eta", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_ETAG;
+ }
+ break;
+ case 'k':
+ if (memeq("lin", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_LINK;
+ }
+ break;
+ case 't':
+ if (memeq("hos", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_HOST;
+ }
+ break;
+ case 'y':
+ if (memeq("var", name, 3)) {
+ return NGHTTP3_QPACK_TOKEN_VARY;
+ }
+ break;
+ }
+ break;
+ case 5:
+ switch (name[4]) {
+ case 'e':
+ if (memeq("rang", name, 4)) {
+ return NGHTTP3_QPACK_TOKEN_RANGE;
+ }
+ break;
+ case 'h':
+ if (memeq(":pat", name, 4)) {
+ return NGHTTP3_QPACK_TOKEN__PATH;
+ }
+ break;
+ }
+ break;
+ case 6:
+ switch (name[5]) {
+ case 'e':
+ if (memeq("cooki", name, 5)) {
+ return NGHTTP3_QPACK_TOKEN_COOKIE;
+ }
+ break;
+ case 'n':
+ if (memeq("origi", name, 5)) {
+ return NGHTTP3_QPACK_TOKEN_ORIGIN;
+ }
+ break;
+ case 'r':
+ if (memeq("serve", name, 5)) {
+ return NGHTTP3_QPACK_TOKEN_SERVER;
+ }
+ break;
+ case 't':
+ if (memeq("accep", name, 5)) {
+ return NGHTTP3_QPACK_TOKEN_ACCEPT;
+ }
+ break;
+ }
+ break;
+ case 7:
+ switch (name[6]) {
+ case 'c':
+ if (memeq("alt-sv", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN_ALT_SVC;
+ }
+ break;
+ case 'd':
+ if (memeq(":metho", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN__METHOD;
+ }
+ break;
+ case 'e':
+ if (memeq(":schem", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN__SCHEME;
+ }
+ if (memeq("purpos", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN_PURPOSE;
+ }
+ if (memeq("upgrad", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN_UPGRADE;
+ }
+ break;
+ case 'r':
+ if (memeq("refere", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN_REFERER;
+ }
+ break;
+ case 's':
+ if (memeq(":statu", name, 6)) {
+ return NGHTTP3_QPACK_TOKEN__STATUS;
+ }
+ break;
+ }
+ break;
+ case 8:
+ switch (name[7]) {
+ case 'e':
+ if (memeq("if-rang", name, 7)) {
+ return NGHTTP3_QPACK_TOKEN_IF_RANGE;
+ }
+ break;
+ case 'n':
+ if (memeq("locatio", name, 7)) {
+ return NGHTTP3_QPACK_TOKEN_LOCATION;
+ }
+ break;
+ case 'y':
+ if (memeq("priorit", name, 7)) {
+ return NGHTTP3_QPACK_TOKEN_PRIORITY;
+ }
+ break;
+ }
+ break;
+ case 9:
+ switch (name[8]) {
+ case 'd':
+ if (memeq("forwarde", name, 8)) {
+ return NGHTTP3_QPACK_TOKEN_FORWARDED;
+ }
+ break;
+ case 'l':
+ if (memeq(":protoco", name, 8)) {
+ return NGHTTP3_QPACK_TOKEN__PROTOCOL;
+ }
+ break;
+ case 't':
+ if (memeq("expect-c", name, 8)) {
+ return NGHTTP3_QPACK_TOKEN_EXPECT_CT;
+ }
+ break;
+ }
+ break;
+ case 10:
+ switch (name[9]) {
+ case 'a':
+ if (memeq("early-dat", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_EARLY_DATA;
+ }
+ break;
+ case 'e':
+ if (memeq("keep-aliv", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_KEEP_ALIVE;
+ }
+ if (memeq("set-cooki", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_SET_COOKIE;
+ }
+ break;
+ case 'n':
+ if (memeq("connectio", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_CONNECTION;
+ }
+ break;
+ case 't':
+ if (memeq("user-agen", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN_USER_AGENT;
+ }
+ break;
+ case 'y':
+ if (memeq(":authorit", name, 9)) {
+ return NGHTTP3_QPACK_TOKEN__AUTHORITY;
+ }
+ break;
+ }
+ break;
+ case 12:
+ switch (name[11]) {
+ case 'e':
+ if (memeq("content-typ", name, 11)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_TYPE;
+ }
+ break;
+ }
+ break;
+ case 13:
+ switch (name[12]) {
+ case 'd':
+ if (memeq("last-modifie", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_LAST_MODIFIED;
+ }
+ break;
+ case 'h':
+ if (memeq("if-none-matc", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH;
+ }
+ break;
+ case 'l':
+ if (memeq("cache-contro", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_CACHE_CONTROL;
+ }
+ break;
+ case 'n':
+ if (memeq("authorizatio", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_AUTHORIZATION;
+ }
+ break;
+ case 's':
+ if (memeq("accept-range", name, 12)) {
+ return NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES;
+ }
+ break;
+ }
+ break;
+ case 14:
+ switch (name[13]) {
+ case 'h':
+ if (memeq("content-lengt", name, 13)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH;
+ }
+ break;
+ }
+ break;
+ case 15:
+ switch (name[14]) {
+ case 'e':
+ if (memeq("accept-languag", name, 14)) {
+ return NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE;
+ }
+ break;
+ case 'g':
+ if (memeq("accept-encodin", name, 14)) {
+ return NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING;
+ }
+ break;
+ case 'r':
+ if (memeq("x-forwarded-fo", name, 14)) {
+ return NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR;
+ }
+ break;
+ case 's':
+ if (memeq("x-frame-option", name, 14)) {
+ return NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS;
+ }
+ break;
+ }
+ break;
+ case 16:
+ switch (name[15]) {
+ case 'g':
+ if (memeq("content-encodin", name, 15)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING;
+ }
+ break;
+ case 'n':
+ if (memeq("proxy-connectio", name, 15)) {
+ return NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION;
+ }
+ if (memeq("x-xss-protectio", name, 15)) {
+ return NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION;
+ }
+ break;
+ }
+ break;
+ case 17:
+ switch (name[16]) {
+ case 'e':
+ if (memeq("if-modified-sinc", name, 16)) {
+ return NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE;
+ }
+ break;
+ case 'g':
+ if (memeq("transfer-encodin", name, 16)) {
+ return NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING;
+ }
+ break;
+ }
+ break;
+ case 19:
+ switch (name[18]) {
+ case 'n':
+ if (memeq("content-dispositio", name, 18)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION;
+ }
+ if (memeq("timing-allow-origi", name, 18)) {
+ return NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN;
+ }
+ break;
+ }
+ break;
+ case 22:
+ switch (name[21]) {
+ case 's':
+ if (memeq("x-content-type-option", name, 21)) {
+ return NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS;
+ }
+ break;
+ }
+ break;
+ case 23:
+ switch (name[22]) {
+ case 'y':
+ if (memeq("content-security-polic", name, 22)) {
+ return NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY;
+ }
+ break;
+ }
+ break;
+ case 25:
+ switch (name[24]) {
+ case 's':
+ if (memeq("upgrade-insecure-request", name, 24)) {
+ return NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS;
+ }
+ break;
+ case 'y':
+ if (memeq("strict-transport-securit", name, 24)) {
+ return NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY;
+ }
+ break;
+ }
+ break;
+ case 27:
+ switch (name[26]) {
+ case 'n':
+ if (memeq("access-control-allow-origi", name, 26)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN;
+ }
+ break;
+ }
+ break;
+ case 28:
+ switch (name[27]) {
+ case 's':
+ if (memeq("access-control-allow-header", name, 27)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS;
+ }
+ if (memeq("access-control-allow-method", name, 27)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS;
+ }
+ break;
+ }
+ break;
+ case 29:
+ switch (name[28]) {
+ case 'd':
+ if (memeq("access-control-request-metho", name, 28)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD;
+ }
+ break;
+ case 's':
+ if (memeq("access-control-expose-header", name, 28)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS;
+ }
+ break;
+ }
+ break;
+ case 30:
+ switch (name[29]) {
+ case 's':
+ if (memeq("access-control-request-header", name, 29)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS;
+ }
+ break;
+ }
+ break;
+ case 32:
+ switch (name[31]) {
+ case 's':
+ if (memeq("access-control-allow-credential", name, 31)) {
+ return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS;
+ }
+ break;
+ }
+ break;
+ }
+ return -1;
+}
+
+static size_t table_space(size_t namelen, size_t valuelen) {
+ return NGHTTP3_QPACK_ENTRY_OVERHEAD + namelen + valuelen;
+}
+
+static int qpack_nv_name_eq(const nghttp3_qpack_nv *a, const nghttp3_nv *b) {
+ return a->name->len == b->namelen &&
+ memeq(a->name->base, b->name, b->namelen);
+}
+
+static int qpack_nv_value_eq(const nghttp3_qpack_nv *a, const nghttp3_nv *b) {
+ return a->value->len == b->valuelen &&
+ memeq(a->value->base, b->value, b->valuelen);
+}
+
+static void qpack_map_init(nghttp3_qpack_map *map) {
+ memset(map, 0, sizeof(nghttp3_qpack_map));
+}
+
+static void qpack_map_insert(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) {
+ nghttp3_qpack_entry **bucket;
+
+ bucket = &map->table[ent->hash & (NGHTTP3_QPACK_MAP_SIZE - 1)];
+
+ if (*bucket == NULL) {
+ *bucket = ent;
+ return;
+ }
+
+ /* larger absidx is linked near the root */
+ ent->map_next = *bucket;
+ *bucket = ent;
+}
+
+static void qpack_map_remove(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) {
+ nghttp3_qpack_entry **dst;
+
+ dst = &map->table[ent->hash & (NGHTTP3_QPACK_MAP_SIZE - 1)];
+
+ for (; *dst; dst = &(*dst)->map_next) {
+ if (*dst != ent) {
+ continue;
+ }
+
+ *dst = ent->map_next;
+ ent->map_next = NULL;
+ return;
+ }
+}
+
+/*
+ * qpack_context_can_reference returns nonzero if dynamic table entry
+ * at |absidx| can be referenced. In other words, it is within
+ * ctx->max_dtable_capacity.
+ */
+static int qpack_context_can_reference(nghttp3_qpack_context *ctx,
+ uint64_t absidx) {
+ nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx);
+ return ctx->dtable_sum - ent->sum <= ctx->max_dtable_capacity;
+}
+
+/* |*ppb_match| (post-base match), if it is not NULL, is always exact
+ match. */
+static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder,
+ int *exact_match,
+ nghttp3_qpack_entry **pmatch,
+ nghttp3_qpack_entry **ppb_match,
+ const nghttp3_nv *nv, int32_t token,
+ uint32_t hash, uint64_t krcnt,
+ int allow_blocking, int name_only) {
+ nghttp3_qpack_entry *p;
+
+ *exact_match = 0;
+ *pmatch = NULL;
+ *ppb_match = NULL;
+
+ for (p = encoder->dtable_map.table[hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; p;
+ p = p->map_next) {
+ if (token != p->nv.token ||
+ (token == -1 && (hash != p->hash || !qpack_nv_name_eq(&p->nv, nv))) ||
+ !qpack_context_can_reference(&encoder->ctx, p->absidx)) {
+ continue;
+ }
+ if (allow_blocking || p->absidx + 1 <= krcnt) {
+ if (!*pmatch) {
+ *pmatch = p;
+ if (name_only) {
+ return;
+ }
+ }
+ if (qpack_nv_value_eq(&p->nv, nv)) {
+ *pmatch = p;
+ *exact_match = 1;
+ return;
+ }
+ } else if (!*ppb_match && qpack_nv_value_eq(&p->nv, nv)) {
+ *ppb_match = p;
+ }
+ }
+}
+
+/*
+ * qpack_context_init initializes |ctx|. |hard_max_dtable_capacity|
+ * is the upper bound of the dynamic table capacity. |mem| is a
+ * memory allocator.
+ *
+ * The maximum dynamic table size is governed by
+ * ctx->max_dtable_capacity and it is initialized to 0.
+ * |hard_max_dtable_capacity| is the upper bound of
+ * ctx->max_dtable_capacity.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_context_init(nghttp3_qpack_context *ctx,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
+ const nghttp3_mem *mem) {
+ int rv;
+ size_t len = 4096 / NGHTTP3_QPACK_ENTRY_OVERHEAD;
+ size_t len2;
+
+ for (len2 = 1; len2 < len; len2 <<= 1)
+ ;
+
+ rv = nghttp3_ringbuf_init(&ctx->dtable, len2, sizeof(nghttp3_qpack_entry *),
+ mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ctx->mem = mem;
+ ctx->dtable_size = 0;
+ ctx->dtable_sum = 0;
+ ctx->hard_max_dtable_capacity = hard_max_dtable_capacity;
+ ctx->max_dtable_capacity = 0;
+ ctx->max_blocked_streams = max_blocked_streams;
+ ctx->next_absidx = 0;
+ ctx->bad = 0;
+
+ return 0;
+}
+
+static void qpack_context_free(nghttp3_qpack_context *ctx) {
+ nghttp3_qpack_entry *ent;
+ size_t i, len = nghttp3_ringbuf_len(&ctx->dtable);
+
+ for (i = 0; i < len; ++i) {
+ ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i);
+ nghttp3_qpack_entry_free(ent);
+ nghttp3_mem_free(ctx->mem, ent);
+ }
+ nghttp3_ringbuf_free(&ctx->dtable);
+}
+
+static int ref_min_cnt_less(const nghttp3_pq_entry *lhsx,
+ const nghttp3_pq_entry *rhsx) {
+ nghttp3_qpack_header_block_ref *lhs =
+ nghttp3_struct_of(lhsx, nghttp3_qpack_header_block_ref, min_cnts_pe);
+ nghttp3_qpack_header_block_ref *rhs =
+ nghttp3_struct_of(rhsx, nghttp3_qpack_header_block_ref, min_cnts_pe);
+
+ return lhs->min_cnt < rhs->min_cnt;
+}
+
+typedef struct nghttp3_blocked_streams_key {
+ uint64_t max_cnt;
+ uint64_t id;
+} nghttp3_blocked_streams_key;
+
+static int max_cnt_greater(const nghttp3_ksl_key *lhs,
+ const nghttp3_ksl_key *rhs) {
+ const nghttp3_blocked_streams_key *a = lhs;
+ const nghttp3_blocked_streams_key *b = rhs;
+ return a->max_cnt > b->max_cnt || (a->max_cnt == b->max_cnt && a->id < b->id);
+}
+
+int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
+ size_t hard_max_dtable_capacity,
+ const nghttp3_mem *mem) {
+ int rv;
+
+ rv = qpack_context_init(&encoder->ctx, hard_max_dtable_capacity, 0, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nghttp3_map_init(&encoder->streams, mem);
+
+ nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater,
+ sizeof(nghttp3_blocked_streams_key), mem);
+
+ qpack_map_init(&encoder->dtable_map);
+ nghttp3_pq_init(&encoder->min_cnts, ref_min_cnt_less, mem);
+
+ encoder->krcnt = 0;
+ encoder->state = NGHTTP3_QPACK_DS_STATE_OPCODE;
+ encoder->opcode = 0;
+ encoder->min_dtable_update = SIZE_MAX;
+ encoder->last_max_dtable_update = 0;
+ encoder->flags = NGHTTP3_QPACK_ENCODER_FLAG_NONE;
+
+ nghttp3_qpack_read_state_reset(&encoder->rstate);
+
+ return 0;
+}
+
+static int map_stream_free(void *data, void *ptr) {
+ const nghttp3_mem *mem = ptr;
+ nghttp3_qpack_stream *stream = data;
+ nghttp3_qpack_stream_del(stream, mem);
+ return 0;
+}
+
+void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder) {
+ nghttp3_pq_free(&encoder->min_cnts);
+ nghttp3_ksl_free(&encoder->blocked_streams);
+ nghttp3_map_each_free(&encoder->streams, map_stream_free,
+ (void *)encoder->ctx.mem);
+ nghttp3_map_free(&encoder->streams);
+ qpack_context_free(&encoder->ctx);
+}
+
+void nghttp3_qpack_encoder_set_max_dtable_capacity(
+ nghttp3_qpack_encoder *encoder, size_t max_dtable_capacity) {
+ max_dtable_capacity =
+ nghttp3_min(max_dtable_capacity, encoder->ctx.hard_max_dtable_capacity);
+
+ if (encoder->ctx.max_dtable_capacity == max_dtable_capacity) {
+ return;
+ }
+
+ encoder->flags |= NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP;
+
+ if (encoder->min_dtable_update > max_dtable_capacity) {
+ encoder->min_dtable_update = max_dtable_capacity;
+ encoder->ctx.max_dtable_capacity = max_dtable_capacity;
+ }
+ encoder->last_max_dtable_update = max_dtable_capacity;
+}
+
+void nghttp3_qpack_encoder_set_max_blocked_streams(
+ nghttp3_qpack_encoder *encoder, size_t max_blocked_streams) {
+ encoder->ctx.max_blocked_streams = max_blocked_streams;
+}
+
+uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder) {
+ assert(!nghttp3_pq_empty(&encoder->min_cnts));
+
+ return nghttp3_struct_of(nghttp3_pq_top(&encoder->min_cnts),
+ nghttp3_qpack_header_block_ref, min_cnts_pe)
+ ->min_cnt;
+}
+
+void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) {
+ nghttp3_ringbuf *dtable = &encoder->ctx.dtable;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ uint64_t min_cnt = UINT64_MAX;
+ size_t len;
+ nghttp3_qpack_entry *ent;
+
+ if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_capacity) {
+ return;
+ }
+
+ if (!nghttp3_pq_empty(&encoder->min_cnts)) {
+ min_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder);
+ }
+
+ for (; encoder->ctx.dtable_size > encoder->ctx.max_dtable_capacity;) {
+ len = nghttp3_ringbuf_len(dtable);
+ ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1);
+ if (ent->absidx + 1 == min_cnt) {
+ return;
+ }
+
+ encoder->ctx.dtable_size -=
+ table_space(ent->nv.name->len, ent->nv.value->len);
+
+ nghttp3_ringbuf_pop_back(dtable);
+ qpack_map_remove(&encoder->dtable_map, ent);
+
+ nghttp3_qpack_entry_free(ent);
+ nghttp3_mem_free(mem, ent);
+ }
+}
+
+/*
+ * qpack_encoder_add_stream_ref adds another dynamic table reference
+ * to a stream denoted by |stream_id|. |stream| must be NULL if no
+ * stream object is not found for the given stream ID. |max_cnt| and
+ * |min_cnt| is the maximum and minimum insert count it references
+ * respectively.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_encoder_add_stream_ref(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id,
+ nghttp3_qpack_stream *stream,
+ uint64_t max_cnt, uint64_t min_cnt) {
+ nghttp3_qpack_header_block_ref *ref;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ uint64_t prev_max_cnt = 0;
+ int rv;
+
+ if (stream == NULL) {
+ rv = nghttp3_qpack_stream_new(&stream, stream_id, mem);
+ if (rv != 0) {
+ assert(rv == NGHTTP3_ERR_NOMEM);
+ return rv;
+ }
+ rv = nghttp3_map_insert(&encoder->streams,
+ (nghttp3_map_key_type)stream->stream_id, stream);
+ if (rv != 0) {
+ assert(rv == NGHTTP3_ERR_NOMEM);
+ nghttp3_qpack_stream_del(stream, mem);
+ return rv;
+ }
+ } else {
+ prev_max_cnt = nghttp3_qpack_stream_get_max_cnt(stream);
+ if (nghttp3_qpack_encoder_stream_is_blocked(encoder, stream) &&
+ max_cnt > prev_max_cnt) {
+ nghttp3_qpack_encoder_unblock_stream(encoder, stream);
+ }
+ }
+
+ rv = nghttp3_qpack_header_block_ref_new(&ref, max_cnt, min_cnt, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = nghttp3_qpack_stream_add_ref(stream, ref);
+ if (rv != 0) {
+ nghttp3_qpack_header_block_ref_del(ref, mem);
+ return rv;
+ }
+
+ if (max_cnt > prev_max_cnt &&
+ nghttp3_qpack_encoder_stream_is_blocked(encoder, stream)) {
+ rv = nghttp3_qpack_encoder_block_stream(encoder, stream);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return nghttp3_pq_push(&encoder->min_cnts, &ref->min_cnts_pe);
+}
+
+static void qpack_encoder_remove_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream) {
+ size_t i, len;
+ nghttp3_qpack_header_block_ref *ref;
+
+ nghttp3_map_remove(&encoder->streams,
+ (nghttp3_map_key_type)stream->stream_id);
+
+ len = nghttp3_ringbuf_len(&stream->refs);
+ for (i = 0; i < len; ++i) {
+ ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs,
+ i);
+
+ assert(ref->min_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX);
+
+ nghttp3_pq_remove(&encoder->min_cnts, &ref->min_cnts_pe);
+ }
+}
+
+/*
+ * reserve_buf_internal ensures that |buf| contains at least
+ * |extra_size| of free space. In other words, if this function
+ * succeeds, nghttp2_buf_left(buf) >= extra_size holds. |min_size| is
+ * the minimum size of buffer. The allocated buffer has at least
+ * |min_size| bytes.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int reserve_buf_internal(nghttp3_buf *buf, size_t extra_size,
+ size_t min_size, const nghttp3_mem *mem) {
+ size_t left = nghttp3_buf_left(buf);
+ size_t n = min_size, need;
+
+ if (left >= extra_size) {
+ return 0;
+ }
+
+ need = nghttp3_buf_cap(buf) + extra_size - left;
+
+ for (; n < need; n *= 2)
+ ;
+
+ return nghttp3_buf_reserve(buf, n, mem);
+}
+
+static int reserve_buf_small(nghttp3_buf *buf, size_t extra_size,
+ const nghttp3_mem *mem) {
+ return reserve_buf_internal(buf, extra_size, 32, mem);
+}
+
+static int reserve_buf(nghttp3_buf *buf, size_t extra_size,
+ const nghttp3_mem *mem) {
+ return reserve_buf_internal(buf, extra_size, 32, mem);
+}
+
+int nghttp3_qpack_encoder_encode(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *pbuf, nghttp3_buf *rbuf,
+ nghttp3_buf *ebuf, int64_t stream_id,
+ const nghttp3_nv *nva, size_t nvlen) {
+ size_t i;
+ uint64_t max_cnt = 0, min_cnt = UINT64_MAX;
+ uint64_t base;
+ int rv = 0;
+ int allow_blocking;
+ int blocked_stream;
+ nghttp3_qpack_stream *stream;
+
+ if (encoder->ctx.bad) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ rv = nghttp3_qpack_encoder_process_dtable_update(encoder, ebuf);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ base = encoder->ctx.next_absidx;
+
+ stream = nghttp3_qpack_encoder_find_stream(encoder, stream_id);
+ blocked_stream =
+ stream && nghttp3_qpack_encoder_stream_is_blocked(encoder, stream);
+ allow_blocking =
+ blocked_stream || encoder->ctx.max_blocked_streams >
+ nghttp3_ksl_len(&encoder->blocked_streams);
+
+ DEBUGF("qpack::encode: stream %ld blocked=%d allow_blocking=%d\n", stream_id,
+ blocked_stream, allow_blocking);
+
+ for (i = 0; i < nvlen; ++i) {
+ rv = nghttp3_qpack_encoder_encode_nv(encoder, &max_cnt, &min_cnt, rbuf,
+ ebuf, &nva[i], base, allow_blocking);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+
+ nghttp3_qpack_encoder_write_field_section_prefix(encoder, pbuf, max_cnt,
+ base);
+
+ /* TODO If max_cnt == 0, no reference is made to dtable. */
+ if (!max_cnt) {
+ return 0;
+ }
+
+ rv = qpack_encoder_add_stream_ref(encoder, stream_id, stream, max_cnt,
+ min_cnt);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ encoder->ctx.bad = 1;
+ return rv;
+}
+
+/*
+ * qpack_write_number writes variable integer to |rbuf|. |num| is an
+ * integer to write. |prefix| is a prefix of variable integer
+ * encoding.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_write_number(nghttp3_buf *rbuf, uint8_t fb, uint64_t num,
+ size_t prefix, const nghttp3_mem *mem) {
+ int rv;
+ size_t len = nghttp3_qpack_put_varint_len(num, prefix);
+ uint8_t *p;
+
+ rv = reserve_buf(rbuf, len, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = rbuf->last;
+
+ *p = fb;
+ p = nghttp3_qpack_put_varint(p, num, prefix);
+
+ assert((size_t)(p - rbuf->last) == len);
+
+ rbuf->last = p;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf) {
+ int rv;
+
+ nghttp3_qpack_encoder_shrink_dtable(encoder);
+
+ if (encoder->ctx.max_dtable_capacity < encoder->ctx.dtable_size ||
+ !(encoder->flags & NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP)) {
+ return 0;
+ }
+
+ if (encoder->min_dtable_update < encoder->last_max_dtable_update) {
+ rv = nghttp3_qpack_encoder_write_set_dtable_cap(encoder, ebuf,
+ encoder->min_dtable_update);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ rv = nghttp3_qpack_encoder_write_set_dtable_cap(
+ encoder, ebuf, encoder->last_max_dtable_update);
+ if (rv != 0) {
+ return rv;
+ }
+
+ encoder->flags &= (uint8_t)~NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP;
+ encoder->min_dtable_update = SIZE_MAX;
+ encoder->ctx.max_dtable_capacity = encoder->last_max_dtable_update;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf, size_t cap) {
+ DEBUGF("qpack::encode: Set Dynamic Table Capacity capacity=%zu\n", cap);
+ return qpack_write_number(ebuf, 0x20, cap, 5, encoder->ctx.mem);
+}
+
+nghttp3_qpack_stream *
+nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id) {
+ return nghttp3_map_find(&encoder->streams, (nghttp3_map_key_type)stream_id);
+}
+
+int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream) {
+ return stream && encoder->krcnt < nghttp3_qpack_stream_get_max_cnt(stream);
+}
+
+/*
+ * qpack_encoder_decide_indexing_mode determines and returns indexing
+ * mode for header field |nv|. |token| is a token of header field
+ * name.
+ */
+static nghttp3_qpack_indexing_mode
+qpack_encoder_decide_indexing_mode(nghttp3_qpack_encoder *encoder,
+ const nghttp3_nv *nv, int32_t token) {
+ if (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) {
+ return NGHTTP3_QPACK_INDEXING_MODE_NEVER;
+ }
+
+ switch (token) {
+ case NGHTTP3_QPACK_TOKEN_AUTHORIZATION:
+ return NGHTTP3_QPACK_INDEXING_MODE_NEVER;
+ case NGHTTP3_QPACK_TOKEN_COOKIE:
+ if (nv->valuelen < 20) {
+ return NGHTTP3_QPACK_INDEXING_MODE_NEVER;
+ }
+ break;
+ case -1:
+ case NGHTTP3_QPACK_TOKEN__PATH:
+ case NGHTTP3_QPACK_TOKEN_AGE:
+ case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH:
+ case NGHTTP3_QPACK_TOKEN_ETAG:
+ case NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE:
+ case NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH:
+ case NGHTTP3_QPACK_TOKEN_LOCATION:
+ case NGHTTP3_QPACK_TOKEN_SET_COOKIE:
+ return NGHTTP3_QPACK_INDEXING_MODE_LITERAL;
+ case NGHTTP3_QPACK_TOKEN_HOST:
+ case NGHTTP3_QPACK_TOKEN_TE:
+ case NGHTTP3_QPACK_TOKEN__PROTOCOL:
+ case NGHTTP3_QPACK_TOKEN_PRIORITY:
+ break;
+ default:
+ if (token >= 1000) {
+ return NGHTTP3_QPACK_INDEXING_MODE_LITERAL;
+ }
+ }
+
+ if (table_space(nv->namelen, nv->valuelen) >
+ encoder->ctx.max_dtable_capacity * 3 / 4) {
+ return NGHTTP3_QPACK_INDEXING_MODE_LITERAL;
+ }
+
+ return NGHTTP3_QPACK_INDEXING_MODE_STORE;
+}
+
+/*
+ * qpack_encoder_can_index returns nonzero if an entry which occupies
+ * |need| bytes can be inserted into dynamic table. |min_cnt| is the
+ * minimum insert count which blocked stream requires.
+ */
+static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need,
+ uint64_t min_cnt) {
+ size_t avail = 0;
+ size_t len;
+ uint64_t gmin_cnt;
+ nghttp3_qpack_entry *min_ent, *last_ent;
+ nghttp3_ringbuf *dtable = &encoder->ctx.dtable;
+
+ if (encoder->ctx.max_dtable_capacity > encoder->ctx.dtable_size) {
+ avail = encoder->ctx.max_dtable_capacity - encoder->ctx.dtable_size;
+ if (need <= avail) {
+ return 1;
+ }
+ }
+
+ if (!nghttp3_pq_empty(&encoder->min_cnts)) {
+ gmin_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder);
+ min_cnt = nghttp3_min(min_cnt, gmin_cnt);
+ }
+
+ if (min_cnt == UINT64_MAX) {
+ return encoder->ctx.max_dtable_capacity >= need;
+ }
+
+ min_ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, min_cnt - 1);
+
+ len = nghttp3_ringbuf_len(&encoder->ctx.dtable);
+ assert(len);
+ last_ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1);
+
+ if (min_ent == last_ent) {
+ return 0;
+ }
+
+ return avail + min_ent->sum - last_ent->sum >= need;
+}
+
+/*
+ * qpack_encoder_can_index_nv returns nonzero if header field |nv| can
+ * be inserted into dynamic table. |min_cnt| is the minimum insert
+ * count which blocked stream requires.
+ */
+static int qpack_encoder_can_index_nv(nghttp3_qpack_encoder *encoder,
+ const nghttp3_nv *nv, uint64_t min_cnt) {
+ return qpack_encoder_can_index(
+ encoder, table_space(nv->namelen, nv->valuelen), min_cnt);
+}
+
+/*
+ * qpack_encoder_can_index_duplicate returns nonzero if an entry at
+ * |absidx| in dynamic table can be inserted to dynamic table as
+ * duplicate. |min_cnt| is the minimum insert count which blocked
+ * stream requires.
+ */
+static int qpack_encoder_can_index_duplicate(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ uint64_t min_cnt) {
+ nghttp3_qpack_entry *ent =
+ nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx);
+
+ return qpack_encoder_can_index(
+ encoder, table_space(ent->nv.name->len, ent->nv.value->len), min_cnt);
+}
+
+/*
+ * qpack_context_check_draining returns nonzero if an entry at
+ * |absidx| in dynamic table is one of draining entries.
+ */
+static int qpack_context_check_draining(nghttp3_qpack_context *ctx,
+ uint64_t absidx) {
+ const size_t safe = ctx->max_dtable_capacity -
+ nghttp3_min(512, ctx->max_dtable_capacity * 1 / 8);
+ nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx);
+
+ return ctx->dtable_sum - ent->sum > safe;
+}
+
+int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder,
+ uint64_t *pmax_cnt, uint64_t *pmin_cnt,
+ nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ const nghttp3_nv *nv, uint64_t base,
+ int allow_blocking) {
+ uint32_t hash = 0;
+ int32_t token;
+ nghttp3_qpack_indexing_mode indexing_mode;
+ nghttp3_qpack_lookup_result sres = {-1, 0, -1}, dres = {-1, 0, -1};
+ nghttp3_qpack_entry *new_ent = NULL;
+ int static_entry;
+ int just_index = 0;
+ int rv;
+
+ token = qpack_lookup_token(nv->name, nv->namelen);
+ static_entry = token != -1 && (size_t)token < nghttp3_arraylen(token_stable);
+ if (static_entry) {
+ hash = token_stable[token].hash;
+ } else {
+ switch (token) {
+ case NGHTTP3_QPACK_TOKEN_HOST:
+ hash = 2952701295u;
+ break;
+ case NGHTTP3_QPACK_TOKEN_TE:
+ hash = 1011170994u;
+ break;
+ case NGHTTP3_QPACK_TOKEN__PROTOCOL:
+ hash = 1128642621u;
+ break;
+ case NGHTTP3_QPACK_TOKEN_PRIORITY:
+ hash = 2498028297u;
+ break;
+ }
+ }
+
+ indexing_mode = qpack_encoder_decide_indexing_mode(encoder, nv, token);
+
+ if (static_entry) {
+ sres = nghttp3_qpack_lookup_stable(nv, token, indexing_mode);
+ if (sres.index != -1 && sres.name_value_match) {
+ return nghttp3_qpack_encoder_write_static_indexed(encoder, rbuf,
+ (size_t)sres.index);
+ }
+ }
+
+ if (hash &&
+ nghttp3_map_size(&encoder->streams) < NGHTTP3_QPACK_MAX_QPACK_STREAMS) {
+ dres = nghttp3_qpack_encoder_lookup_dtable(encoder, nv, token, hash,
+ indexing_mode, encoder->krcnt,
+ allow_blocking);
+ just_index = indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_STORE &&
+ dres.pb_index == -1;
+ }
+
+ if (dres.index != -1 && dres.name_value_match) {
+ if (allow_blocking &&
+ qpack_context_check_draining(&encoder->ctx, (size_t)dres.index) &&
+ qpack_encoder_can_index_duplicate(encoder, (size_t)dres.index,
+ *pmin_cnt)) {
+ rv = nghttp3_qpack_encoder_write_duplicate_insert(encoder, ebuf,
+ (size_t)dres.index);
+ if (rv != 0) {
+ return rv;
+ }
+ rv = nghttp3_qpack_encoder_dtable_duplicate_add(encoder,
+ (size_t)dres.index);
+ if (rv != 0) {
+ return rv;
+ }
+
+ new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx);
+ dres.index = (nghttp3_ssize)new_ent->absidx;
+ }
+ *pmax_cnt = nghttp3_max(*pmax_cnt, (size_t)(dres.index + 1));
+ *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)(dres.index + 1));
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed(
+ encoder, rbuf, (size_t)dres.index, base);
+ }
+
+ if (sres.index != -1) {
+ if (just_index && qpack_encoder_can_index_nv(encoder, nv, *pmin_cnt)) {
+ rv = nghttp3_qpack_encoder_write_static_insert(encoder, ebuf,
+ (size_t)sres.index, nv);
+ if (rv != 0) {
+ return rv;
+ }
+ rv = nghttp3_qpack_encoder_dtable_static_add(encoder, (size_t)sres.index,
+ nv, hash);
+ if (rv != 0) {
+ return rv;
+ }
+ if (allow_blocking) {
+ new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx);
+ *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1);
+ *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1);
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed(
+ encoder, rbuf, new_ent->absidx, base);
+ }
+ }
+
+ return nghttp3_qpack_encoder_write_static_indexed_name(
+ encoder, rbuf, (size_t)sres.index, nv);
+ }
+
+ if (dres.index != -1) {
+ if (just_index &&
+ qpack_encoder_can_index_nv(
+ encoder, nv,
+ allow_blocking ? *pmin_cnt
+ : nghttp3_min((size_t)dres.index + 1, *pmin_cnt))) {
+ rv = nghttp3_qpack_encoder_write_dynamic_insert(encoder, ebuf,
+ (size_t)dres.index, nv);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (!allow_blocking) {
+ *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)dres.index + 1);
+ }
+
+ rv = nghttp3_qpack_encoder_dtable_dynamic_add(encoder, (size_t)dres.index,
+ nv, hash);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (allow_blocking) {
+ new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx);
+ *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1);
+ *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1);
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed(
+ encoder, rbuf, new_ent->absidx, base);
+ }
+ }
+
+ *pmax_cnt = nghttp3_max(*pmax_cnt, (size_t)(dres.index + 1));
+ *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)(dres.index + 1));
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed_name(
+ encoder, rbuf, (size_t)dres.index, base, nv);
+ }
+
+ if (just_index && qpack_encoder_can_index_nv(encoder, nv, *pmin_cnt)) {
+ rv = nghttp3_qpack_encoder_dtable_literal_add(encoder, nv, token, hash);
+ if (rv != 0) {
+ return rv;
+ }
+ rv = nghttp3_qpack_encoder_write_literal_insert(encoder, ebuf, nv);
+ if (rv != 0) {
+ return rv;
+ }
+ if (allow_blocking) {
+ new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx);
+ *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1);
+ *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1);
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed(encoder, rbuf,
+ new_ent->absidx, base);
+ }
+ }
+
+ return nghttp3_qpack_encoder_write_literal(encoder, rbuf, nv);
+}
+
+nghttp3_qpack_lookup_result
+nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token,
+ nghttp3_qpack_indexing_mode indexing_mode) {
+ nghttp3_qpack_lookup_result res = {(nghttp3_ssize)token_stable[token].absidx,
+ 0, -1};
+ nghttp3_qpack_static_entry *ent;
+ nghttp3_qpack_static_header *hdr;
+ size_t i;
+
+ assert(token >= 0);
+
+ if (indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_NEVER) {
+ return res;
+ }
+
+ for (i = (size_t)token;
+ i < nghttp3_arraylen(token_stable) && token_stable[i].token == token;
+ ++i) {
+ ent = &token_stable[i];
+ hdr = &stable[ent->absidx];
+ if (hdr->value.len == nv->valuelen &&
+ memeq(hdr->value.base, nv->value, nv->valuelen)) {
+ res.index = (nghttp3_ssize)ent->absidx;
+ res.name_value_match = 1;
+ return res;
+ }
+ }
+ return res;
+}
+
+nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable(
+ nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token,
+ uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, uint64_t krcnt,
+ int allow_blocking) {
+ nghttp3_qpack_lookup_result res = {-1, 0, -1};
+ int exact_match = 0;
+ nghttp3_qpack_entry *match, *pb_match;
+
+ encoder_qpack_map_find(encoder, &exact_match, &match, &pb_match, nv, token,
+ hash, krcnt, allow_blocking,
+ indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_NEVER);
+ if (match) {
+ res.index = (nghttp3_ssize)match->absidx;
+ res.name_value_match = exact_match;
+ }
+ if (pb_match) {
+ res.pb_index = (nghttp3_ssize)pb_match->absidx;
+ }
+
+ return res;
+}
+
+int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref,
+ uint64_t max_cnt, uint64_t min_cnt,
+ const nghttp3_mem *mem) {
+ nghttp3_qpack_header_block_ref *ref =
+ nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_header_block_ref));
+
+ if (ref == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ ref->max_cnts_pe.index = NGHTTP3_PQ_BAD_INDEX;
+ ref->min_cnts_pe.index = NGHTTP3_PQ_BAD_INDEX;
+ ref->max_cnt = max_cnt;
+ ref->min_cnt = min_cnt;
+
+ *pref = ref;
+
+ return 0;
+}
+
+void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref,
+ const nghttp3_mem *mem) {
+ nghttp3_mem_free(mem, ref);
+}
+
+static int ref_max_cnt_greater(const nghttp3_pq_entry *lhsx,
+ const nghttp3_pq_entry *rhsx) {
+ const nghttp3_qpack_header_block_ref *lhs =
+ nghttp3_struct_of(lhsx, nghttp3_qpack_header_block_ref, max_cnts_pe);
+ const nghttp3_qpack_header_block_ref *rhs =
+ nghttp3_struct_of(rhsx, nghttp3_qpack_header_block_ref, max_cnts_pe);
+
+ return lhs->max_cnt > rhs->max_cnt;
+}
+
+int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id,
+ const nghttp3_mem *mem) {
+ int rv;
+ nghttp3_qpack_stream *stream;
+
+ stream = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream));
+ if (stream == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ rv = nghttp3_ringbuf_init(&stream->refs, 4,
+ sizeof(nghttp3_qpack_header_block_ref *), mem);
+ if (rv != 0) {
+ nghttp3_mem_free(mem, stream);
+ return rv;
+ }
+
+ nghttp3_pq_init(&stream->max_cnts, ref_max_cnt_greater, mem);
+
+ stream->stream_id = stream_id;
+
+ *pstream = stream;
+
+ return 0;
+}
+
+void nghttp3_qpack_stream_del(nghttp3_qpack_stream *stream,
+ const nghttp3_mem *mem) {
+ nghttp3_qpack_header_block_ref *ref;
+ size_t i, len;
+
+ if (stream == NULL) {
+ return;
+ }
+
+ nghttp3_pq_free(&stream->max_cnts);
+
+ len = nghttp3_ringbuf_len(&stream->refs);
+ for (i = 0; i < len; ++i) {
+ ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs,
+ i);
+ nghttp3_qpack_header_block_ref_del(ref, mem);
+ }
+
+ nghttp3_ringbuf_free(&stream->refs);
+
+ nghttp3_mem_free(mem, stream);
+}
+
+uint64_t nghttp3_qpack_stream_get_max_cnt(const nghttp3_qpack_stream *stream) {
+ nghttp3_qpack_header_block_ref *ref;
+
+ if (nghttp3_pq_empty(&stream->max_cnts)) {
+ return 0;
+ }
+
+ ref = nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
+ nghttp3_qpack_header_block_ref, max_cnts_pe);
+ return ref->max_cnt;
+}
+
+int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream,
+ nghttp3_qpack_header_block_ref *ref) {
+ nghttp3_qpack_header_block_ref **dest;
+ int rv;
+
+ if (nghttp3_ringbuf_full(&stream->refs)) {
+ rv = nghttp3_ringbuf_reserve(&stream->refs,
+ nghttp3_ringbuf_len(&stream->refs) * 2);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ dest = nghttp3_ringbuf_push_back(&stream->refs);
+ *dest = ref;
+
+ return nghttp3_pq_push(&stream->max_cnts, &ref->max_cnts_pe);
+}
+
+void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream) {
+ nghttp3_qpack_header_block_ref *ref;
+
+ assert(nghttp3_ringbuf_len(&stream->refs));
+
+ ref =
+ *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0);
+
+ assert(ref->max_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX);
+
+ nghttp3_pq_remove(&stream->max_cnts, &ref->max_cnts_pe);
+
+ nghttp3_ringbuf_pop_front(&stream->refs);
+}
+
+int nghttp3_qpack_encoder_write_static_indexed(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ uint64_t absidx) {
+ DEBUGF("qpack::encode: Indexed Field Line (static) absidx=%" PRIu64 "\n",
+ absidx);
+ return qpack_write_number(rbuf, 0xc0, absidx, 6, encoder->ctx.mem);
+}
+
+int nghttp3_qpack_encoder_write_dynamic_indexed(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ uint64_t absidx,
+ uint64_t base) {
+ DEBUGF("qpack::encode: Indexed Field Line (dynamic) absidx=%" PRIu64
+ " base=%" PRIu64 "\n",
+ absidx, base);
+
+ if (absidx < base) {
+ return qpack_write_number(rbuf, 0x80, base - absidx - 1, 6,
+ encoder->ctx.mem);
+ }
+
+ return qpack_write_number(rbuf, 0x10, absidx - base, 4, encoder->ctx.mem);
+}
+
+/*
+ * qpack_encoder_write_indexed_name writes generic indexed name. |fb|
+ * is the first byte. |nameidx| is an index of referenced name.
+ * |prefix| is a prefix of variable integer encoding. |nv| is a
+ * header field to encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_encoder_write_indexed_name(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *buf, uint8_t fb,
+ uint64_t nameidx, size_t prefix,
+ const nghttp3_nv *nv) {
+ int rv;
+ size_t len = nghttp3_qpack_put_varint_len(nameidx, prefix);
+ uint8_t *p;
+ size_t hlen;
+ int h = 0;
+
+ hlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen);
+ if (hlen < nv->valuelen) {
+ h = 1;
+ len += nghttp3_qpack_put_varint_len(hlen, 7) + hlen;
+ } else {
+ len += nghttp3_qpack_put_varint_len(nv->valuelen, 7) + nv->valuelen;
+ }
+
+ rv = reserve_buf(buf, len, encoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = buf->last;
+
+ *p = fb;
+ p = nghttp3_qpack_put_varint(p, nameidx, prefix);
+
+ if (h) {
+ *p = 0x80;
+ p = nghttp3_qpack_put_varint(p, hlen, 7);
+ p = nghttp3_qpack_huffman_encode(p, nv->value, nv->valuelen);
+ } else {
+ *p = 0;
+ p = nghttp3_qpack_put_varint(p, nv->valuelen, 7);
+ if (nv->valuelen) {
+ p = nghttp3_cpymem(p, nv->value, nv->valuelen);
+ }
+ }
+
+ assert((size_t)(p - buf->last) == len);
+
+ buf->last = p;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_write_static_indexed_name(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx,
+ const nghttp3_nv *nv) {
+ uint8_t fb =
+ (uint8_t)(0x50 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x20 : 0));
+
+ DEBUGF("qpack::encode: Literal Field Line With Name Reference (static) "
+ "absidx=%" PRIu64 " never=%d\n",
+ absidx, (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) != 0);
+ return qpack_encoder_write_indexed_name(encoder, rbuf, fb, absidx, 4, nv);
+}
+
+int nghttp3_qpack_encoder_write_dynamic_indexed_name(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx,
+ uint64_t base, const nghttp3_nv *nv) {
+ uint8_t fb;
+
+ DEBUGF("qpack::encode: Literal Field Line With Name Reference (dynamic) "
+ "absidx=%" PRIu64 " base=%" PRIu64 " never=%d\n",
+ absidx, base, (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) != 0);
+
+ if (absidx < base) {
+ fb = (uint8_t)(0x40 |
+ ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x20 : 0));
+ return qpack_encoder_write_indexed_name(encoder, rbuf, fb,
+ base - absidx - 1, 4, nv);
+ }
+
+ fb = (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x08 : 0;
+ return qpack_encoder_write_indexed_name(encoder, rbuf, fb, absidx - base, 3,
+ nv);
+}
+
+/*
+ * qpack_encoder_write_literal writes generic literal header field
+ * representation. |fb| is a first byte. |prefix| is a prefix of
+ * variable integer encoding for name length. |nv| is a header field
+ * to encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *buf, uint8_t fb,
+ size_t prefix, const nghttp3_nv *nv) {
+ int rv;
+ size_t len;
+ uint8_t *p;
+ size_t nhlen, vhlen;
+ int nh = 0, vh = 0;
+
+ nhlen = nghttp3_qpack_huffman_encode_count(nv->name, nv->namelen);
+ if (nhlen < nv->namelen) {
+ nh = 1;
+ len = nghttp3_qpack_put_varint_len(nhlen, prefix) + nhlen;
+ } else {
+ len = nghttp3_qpack_put_varint_len(nv->namelen, prefix) + nv->namelen;
+ }
+
+ vhlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen);
+ if (vhlen < nv->valuelen) {
+ vh = 1;
+ len += nghttp3_qpack_put_varint_len(vhlen, 7) + vhlen;
+ } else {
+ len += nghttp3_qpack_put_varint_len(nv->valuelen, 7) + nv->valuelen;
+ }
+
+ rv = reserve_buf(buf, len, encoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = buf->last;
+
+ *p = fb;
+ if (nh) {
+ *p |= (uint8_t)(1 << prefix);
+ p = nghttp3_qpack_put_varint(p, nhlen, prefix);
+ p = nghttp3_qpack_huffman_encode(p, nv->name, nv->namelen);
+ } else {
+ p = nghttp3_qpack_put_varint(p, nv->namelen, prefix);
+ if (nv->namelen) {
+ p = nghttp3_cpymem(p, nv->name, nv->namelen);
+ }
+ }
+
+ *p = 0;
+
+ if (vh) {
+ *p |= 0x80;
+ p = nghttp3_qpack_put_varint(p, vhlen, 7);
+ p = nghttp3_qpack_huffman_encode(p, nv->value, nv->valuelen);
+ } else {
+ p = nghttp3_qpack_put_varint(p, nv->valuelen, 7);
+ if (nv->valuelen) {
+ p = nghttp3_cpymem(p, nv->value, nv->valuelen);
+ }
+ }
+
+ assert((size_t)(p - buf->last) == len);
+
+ buf->last = p;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ const nghttp3_nv *nv) {
+ uint8_t fb =
+ (uint8_t)(0x20 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x10 : 0));
+
+ DEBUGF("qpack::encode: Literal Field Line With Literal Name\n");
+ return qpack_encoder_write_literal(encoder, rbuf, fb, 3, nv);
+}
+
+int nghttp3_qpack_encoder_write_static_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx,
+ const nghttp3_nv *nv) {
+ DEBUGF("qpack::encode: Insert With Name Reference (static) absidx=%" PRIu64
+ "\n",
+ absidx);
+ return qpack_encoder_write_indexed_name(encoder, ebuf, 0xc0, absidx, 6, nv);
+}
+
+int nghttp3_qpack_encoder_write_dynamic_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx,
+ const nghttp3_nv *nv) {
+ DEBUGF("qpack::encode: Insert With Name Reference (dynamic) absidx=%" PRIu64
+ "\n",
+ absidx);
+ return qpack_encoder_write_indexed_name(
+ encoder, ebuf, 0x80, encoder->ctx.next_absidx - absidx - 1, 6, nv);
+}
+
+int nghttp3_qpack_encoder_write_duplicate_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx) {
+ uint64_t idx = encoder->ctx.next_absidx - absidx - 1;
+ size_t len = nghttp3_qpack_put_varint_len(idx, 5);
+ uint8_t *p;
+ int rv;
+
+ DEBUGF("qpack::encode: Insert duplicate absidx=%" PRIu64 "\n", absidx);
+
+ rv = reserve_buf(ebuf, len, encoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = ebuf->last;
+
+ *p = 0;
+ p = nghttp3_qpack_put_varint(p, idx, 5);
+
+ assert((size_t)(p - ebuf->last) == len);
+
+ ebuf->last = p;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_write_literal_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ const nghttp3_nv *nv) {
+ DEBUGF("qpack::encode: Insert With Literal Name\n");
+ return qpack_encoder_write_literal(encoder, ebuf, 0x40, 5, nv);
+}
+
+int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx,
+ nghttp3_qpack_nv *qnv,
+ nghttp3_qpack_map *dtable_map,
+ uint32_t hash) {
+ nghttp3_qpack_entry *new_ent, **p, *ent;
+ const nghttp3_mem *mem = ctx->mem;
+ size_t space;
+ size_t i;
+ int rv;
+
+ space = table_space(qnv->name->len, qnv->value->len);
+
+ assert(space <= ctx->max_dtable_capacity);
+
+ while (ctx->dtable_size + space > ctx->max_dtable_capacity) {
+ i = nghttp3_ringbuf_len(&ctx->dtable);
+ assert(i);
+ ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1);
+
+ ctx->dtable_size -= table_space(ent->nv.name->len, ent->nv.value->len);
+
+ nghttp3_ringbuf_pop_back(&ctx->dtable);
+ if (dtable_map) {
+ qpack_map_remove(dtable_map, ent);
+ }
+
+ nghttp3_qpack_entry_free(ent);
+ nghttp3_mem_free(mem, ent);
+ }
+
+ new_ent = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_entry));
+ if (new_ent == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ nghttp3_qpack_entry_init(new_ent, qnv, ctx->dtable_sum, ctx->next_absidx++,
+ hash);
+
+ if (nghttp3_ringbuf_full(&ctx->dtable)) {
+ rv = nghttp3_ringbuf_reserve(&ctx->dtable,
+ nghttp3_ringbuf_len(&ctx->dtable) * 2);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+
+ p = nghttp3_ringbuf_push_front(&ctx->dtable);
+ *p = new_ent;
+
+ if (dtable_map) {
+ qpack_map_insert(dtable_map, new_ent);
+ }
+
+ ctx->dtable_size += space;
+ ctx->dtable_sum += space;
+
+ return 0;
+
+fail:
+ nghttp3_qpack_entry_free(new_ent);
+ nghttp3_mem_free(mem, new_ent);
+
+ return rv;
+}
+
+int nghttp3_qpack_encoder_dtable_static_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ const nghttp3_nv *nv,
+ uint32_t hash) {
+ const nghttp3_qpack_static_header *shd;
+ nghttp3_qpack_nv qnv;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ int rv;
+
+ rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ assert(nghttp3_arraylen(stable) > absidx);
+
+ shd = &stable[absidx];
+
+ qnv.name = (nghttp3_rcbuf *)&shd->name;
+ qnv.token = shd->token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv,
+ &encoder->dtable_map, hash);
+
+ nghttp3_rcbuf_decref(qnv.value);
+
+ return rv;
+}
+
+int nghttp3_qpack_encoder_dtable_dynamic_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ const nghttp3_nv *nv,
+ uint32_t hash) {
+ nghttp3_qpack_nv qnv;
+ nghttp3_qpack_entry *ent;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ int rv;
+
+ rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx);
+
+ qnv.name = ent->nv.name;
+ qnv.token = ent->nv.token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ nghttp3_rcbuf_incref(qnv.name);
+
+ rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv,
+ &encoder->dtable_map, hash);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+int nghttp3_qpack_encoder_dtable_duplicate_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx) {
+ nghttp3_qpack_nv qnv;
+ nghttp3_qpack_entry *ent;
+ int rv;
+
+ ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx);
+
+ qnv = ent->nv;
+ nghttp3_rcbuf_incref(qnv.name);
+ nghttp3_rcbuf_incref(qnv.value);
+
+ rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv,
+ &encoder->dtable_map, ent->hash);
+
+ nghttp3_rcbuf_decref(qnv.name);
+ nghttp3_rcbuf_decref(qnv.value);
+
+ return rv;
+}
+
+int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder,
+ const nghttp3_nv *nv,
+ int32_t token, uint32_t hash) {
+ nghttp3_qpack_nv qnv;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ int rv;
+
+ rv = nghttp3_rcbuf_new2(&qnv.name, nv->name, nv->namelen, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem);
+ if (rv != 0) {
+ nghttp3_rcbuf_decref(qnv.name);
+ return rv;
+ }
+
+ qnv.token = token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv,
+ &encoder->dtable_map, hash);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+nghttp3_qpack_entry *
+nghttp3_qpack_context_dtable_get(nghttp3_qpack_context *ctx, uint64_t absidx) {
+ size_t relidx;
+
+ assert(ctx->next_absidx > absidx);
+ assert(ctx->next_absidx - absidx - 1 < nghttp3_ringbuf_len(&ctx->dtable));
+
+ relidx = (size_t)(ctx->next_absidx - absidx - 1);
+
+ return *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, relidx);
+}
+
+nghttp3_qpack_entry *
+nghttp3_qpack_context_dtable_top(nghttp3_qpack_context *ctx) {
+ assert(nghttp3_ringbuf_len(&ctx->dtable));
+ return *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, 0);
+}
+
+void nghttp3_qpack_entry_init(nghttp3_qpack_entry *ent, nghttp3_qpack_nv *qnv,
+ size_t sum, uint64_t absidx, uint32_t hash) {
+ ent->nv = *qnv;
+ ent->map_next = NULL;
+ ent->sum = sum;
+ ent->absidx = absidx;
+ ent->hash = hash;
+
+ nghttp3_rcbuf_incref(ent->nv.name);
+ nghttp3_rcbuf_incref(ent->nv.value);
+}
+
+void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent) {
+ nghttp3_rcbuf_decref(ent->nv.value);
+ nghttp3_rcbuf_decref(ent->nv.name);
+}
+
+int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream) {
+ nghttp3_blocked_streams_key bsk = {
+ nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
+ nghttp3_qpack_header_block_ref, max_cnts_pe)
+ ->max_cnt,
+ (uint64_t)stream->stream_id};
+
+ return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, &bsk, stream);
+}
+
+void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream) {
+ nghttp3_blocked_streams_key bsk = {
+ nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
+ nghttp3_qpack_header_block_ref, max_cnts_pe)
+ ->max_cnt,
+ (uint64_t)stream->stream_id};
+ nghttp3_ksl_it it;
+
+ /* This is purely debugging purpose only */
+ it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk);
+
+ assert(!nghttp3_ksl_it_end(&it));
+ assert(nghttp3_ksl_it_get(&it) == stream);
+
+ nghttp3_ksl_remove_hint(&encoder->blocked_streams, NULL, &it, &bsk);
+}
+
+void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder,
+ uint64_t max_cnt) {
+ nghttp3_blocked_streams_key bsk = {max_cnt, 0};
+ nghttp3_ksl_it it;
+
+ it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk);
+
+ for (; !nghttp3_ksl_it_end(&it);) {
+ bsk = *(nghttp3_blocked_streams_key *)nghttp3_ksl_it_key(&it);
+ nghttp3_ksl_remove_hint(&encoder->blocked_streams, &it, &it, &bsk);
+ }
+}
+
+int nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id) {
+ nghttp3_qpack_stream *stream =
+ nghttp3_qpack_encoder_find_stream(encoder, stream_id);
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ nghttp3_qpack_header_block_ref *ref;
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR;
+ }
+
+ assert(nghttp3_ringbuf_len(&stream->refs));
+
+ ref =
+ *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0);
+
+ DEBUGF("qpack::encoder: Header acknowledgement stream=%ld ricnt=%" PRIu64
+ " krcnt=%" PRIu64 "\n",
+ stream_id, ref->max_cnt, encoder->krcnt);
+
+ if (encoder->krcnt < ref->max_cnt) {
+ encoder->krcnt = ref->max_cnt;
+
+ nghttp3_qpack_encoder_unblock(encoder, ref->max_cnt);
+ }
+
+ nghttp3_qpack_stream_pop_ref(stream);
+
+ assert(ref->min_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX);
+
+ nghttp3_pq_remove(&encoder->min_cnts, &ref->min_cnts_pe);
+
+ nghttp3_qpack_header_block_ref_del(ref, mem);
+
+ if (nghttp3_ringbuf_len(&stream->refs)) {
+ return 0;
+ }
+
+ qpack_encoder_remove_stream(encoder, stream);
+
+ nghttp3_qpack_stream_del(stream, mem);
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_add_icnt(nghttp3_qpack_encoder *encoder, uint64_t n) {
+ if (n == 0 || encoder->ctx.next_absidx - encoder->krcnt < n) {
+ return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR;
+ }
+ encoder->krcnt += n;
+
+ nghttp3_qpack_encoder_unblock(encoder, encoder->krcnt);
+
+ return 0;
+}
+
+void nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder) {
+ encoder->krcnt = encoder->ctx.next_absidx;
+
+ nghttp3_ksl_clear(&encoder->blocked_streams);
+ nghttp3_pq_clear(&encoder->min_cnts);
+ nghttp3_map_each_free(&encoder->streams, map_stream_free,
+ (void *)encoder->ctx.mem);
+ nghttp3_map_clear(&encoder->streams);
+}
+
+void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id) {
+ nghttp3_qpack_stream *stream =
+ nghttp3_qpack_encoder_find_stream(encoder, stream_id);
+ const nghttp3_mem *mem = encoder->ctx.mem;
+
+ if (stream == NULL) {
+ return;
+ }
+
+ if (nghttp3_qpack_encoder_stream_is_blocked(encoder, stream)) {
+ nghttp3_qpack_encoder_unblock_stream(encoder, stream);
+ }
+
+ qpack_encoder_remove_stream(encoder, stream);
+
+ nghttp3_qpack_stream_del(stream, mem);
+}
+
+size_t
+nghttp3_qpack_encoder_get_num_blocked_streams(nghttp3_qpack_encoder *encoder) {
+ return nghttp3_ksl_len(&encoder->blocked_streams);
+}
+
+int nghttp3_qpack_encoder_write_field_section_prefix(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt,
+ uint64_t base) {
+ size_t max_ents =
+ encoder->ctx.hard_max_dtable_capacity / NGHTTP3_QPACK_ENTRY_OVERHEAD;
+ uint64_t encricnt = ricnt == 0 ? 0 : (ricnt % (2 * max_ents)) + 1;
+ int sign = base < ricnt;
+ uint64_t delta_base = sign ? ricnt - base - 1 : base - ricnt;
+ size_t len = nghttp3_qpack_put_varint_len(encricnt, 8) +
+ nghttp3_qpack_put_varint_len(delta_base, 7);
+ uint8_t *p;
+ int rv;
+
+ DEBUGF("qpack::encode: ricnt=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 "\n",
+ ricnt, base, encoder->ctx.next_absidx);
+
+ rv = reserve_buf(pbuf, len, encoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = pbuf->last;
+
+ p = nghttp3_qpack_put_varint(p, encricnt, 8);
+ if (sign) {
+ *p = 0x80;
+ } else {
+ *p = 0;
+ }
+ p = nghttp3_qpack_put_varint(p, delta_base, 7);
+
+ assert((size_t)(p - pbuf->last) == len);
+
+ pbuf->last = p;
+
+ return 0;
+}
+
+/*
+ * qpack_read_varint reads |rstate->prefix| prefixed integer stored
+ * from |begin|. The |end| represents the 1 beyond the last of the
+ * valid contiguous memory region from |begin|. The decoded integer
+ * must be less than or equal to NGHTTP3_QPACK_INT_MAX.
+ *
+ * If the |rstate->left| is nonzero, it is used as an initial value,
+ * and this function assumes the |begin| starts with intermediate
+ * data. |rstate->shift| is used as initial integer shift.
+ *
+ * If an entire integer is decoded successfully, the |*fin| is set to
+ * nonzero.
+ *
+ * This function stores the decoded integer in |rstate->left| if it
+ * succeeds, including partial decoding (in this case, number of shift
+ * to make in the next call will be stored in |rstate->shift|) and
+ * returns number of bytes processed, or returns negative error code
+ * NGHTTP3_ERR_QPACK_FATAL, indicating decoding error.
+ */
+static nghttp3_ssize qpack_read_varint(int *fin,
+ nghttp3_qpack_read_state *rstate,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ uint64_t k = (uint8_t)((1 << rstate->prefix) - 1);
+ uint64_t n = rstate->left;
+ uint64_t add;
+ const uint8_t *p = begin;
+ size_t shift = rstate->shift;
+
+ rstate->shift = 0;
+ *fin = 0;
+
+ if (n == 0) {
+ if (((*p) & k) != k) {
+ rstate->left = (*p) & k;
+ *fin = 1;
+ return 1;
+ }
+
+ n = k;
+
+ if (++p == end) {
+ rstate->left = n;
+ return (nghttp3_ssize)(p - begin);
+ }
+ }
+
+ for (; p != end; ++p, shift += 7) {
+ add = (*p) & 0x7f;
+
+ if (shift > 62) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ if ((NGHTTP3_QPACK_INT_MAX >> shift) < add) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ add <<= shift;
+
+ if (NGHTTP3_QPACK_INT_MAX - add < n) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ n += add;
+
+ if (((*p) & (1 << 7)) == 0) {
+ break;
+ }
+ }
+
+ rstate->shift = shift;
+
+ if (p == end) {
+ rstate->left = n;
+ return (nghttp3_ssize)(p - begin);
+ }
+
+ rstate->left = n;
+ *fin = 1;
+ return (nghttp3_ssize)(p + 1 - begin);
+}
+
+nghttp3_ssize nghttp3_qpack_encoder_read_decoder(nghttp3_qpack_encoder *encoder,
+ const uint8_t *src,
+ size_t srclen) {
+ const uint8_t *p = src, *end;
+ int rv;
+ nghttp3_ssize nread;
+ int rfin;
+
+ if (encoder->ctx.bad) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ if (srclen == 0) {
+ return 0;
+ }
+
+ end = src + srclen;
+
+ for (; p != end;) {
+ switch (encoder->state) {
+ case NGHTTP3_QPACK_DS_STATE_OPCODE:
+ if ((*p) & 0x80) {
+ DEBUGF("qpack::encode: OPCODE_SECTION_ACK\n");
+ encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK;
+ encoder->rstate.prefix = 7;
+ } else if ((*p) & 0x40) {
+ DEBUGF("qpack::encode: OPCODE_STREAM_CANCEL\n");
+ encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL;
+ encoder->rstate.prefix = 6;
+ } else {
+ DEBUGF("qpack::encode: OPCODE_ICNT_INCREMENT\n");
+ encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT;
+ encoder->rstate.prefix = 6;
+ }
+ encoder->state = NGHTTP3_QPACK_DS_STATE_READ_NUMBER;
+ /* fall through */
+ case NGHTTP3_QPACK_DS_STATE_READ_NUMBER:
+ nread = qpack_read_varint(&rfin, &encoder->rstate, p, end);
+ if (nread < 0) {
+ assert(nread == NGHTTP3_ERR_QPACK_FATAL);
+ rv = NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ return p - src;
+ }
+
+ switch (encoder->opcode) {
+ case NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT:
+ rv = nghttp3_qpack_encoder_add_icnt(encoder, encoder->rstate.left);
+ if (rv != 0) {
+ goto fail;
+ }
+ break;
+ case NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK:
+ rv = nghttp3_qpack_encoder_ack_header(encoder,
+ (int64_t)encoder->rstate.left);
+ if (rv != 0) {
+ goto fail;
+ }
+ break;
+ case NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL:
+ nghttp3_qpack_encoder_cancel_stream(encoder,
+ (int64_t)encoder->rstate.left);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ encoder->state = NGHTTP3_QPACK_DS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&encoder->rstate);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+ }
+
+ return p - src;
+
+fail:
+ encoder->ctx.bad = 1;
+ return rv;
+}
+
+size_t nghttp3_qpack_put_varint_len(uint64_t n, size_t prefix) {
+ size_t k = (size_t)((1 << prefix) - 1);
+ size_t len = 0;
+
+ if (n < k) {
+ return 1;
+ }
+
+ n -= k;
+ ++len;
+
+ for (; n >= 128; n >>= 7, ++len)
+ ;
+
+ return len + 1;
+}
+
+uint8_t *nghttp3_qpack_put_varint(uint8_t *buf, uint64_t n, size_t prefix) {
+ size_t k = (size_t)((1 << prefix) - 1);
+
+ *buf = (uint8_t)(*buf & ~k);
+
+ if (n < k) {
+ *buf = (uint8_t)(*buf | n);
+ return buf + 1;
+ }
+
+ *buf = (uint8_t)(*buf | k);
+ ++buf;
+
+ n -= k;
+
+ for (; n >= 128; n >>= 7) {
+ *buf++ = (uint8_t)((1 << 7) | (n & 0x7f));
+ }
+
+ *buf++ = (uint8_t)n;
+
+ return buf;
+}
+
+void nghttp3_qpack_read_state_free(nghttp3_qpack_read_state *rstate) {
+ nghttp3_rcbuf_decref(rstate->value);
+ nghttp3_rcbuf_decref(rstate->name);
+}
+
+void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate) {
+ rstate->name = NULL;
+ rstate->value = NULL;
+ nghttp3_buf_init(&rstate->namebuf);
+ nghttp3_buf_init(&rstate->valuebuf);
+ rstate->left = 0;
+ rstate->prefix = 0;
+ rstate->shift = 0;
+ rstate->absidx = 0;
+ rstate->never = 0;
+ rstate->dynamic = 0;
+ rstate->huffman_encoded = 0;
+}
+
+int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
+ const nghttp3_mem *mem) {
+ int rv;
+
+ rv = qpack_context_init(&decoder->ctx, hard_max_dtable_capacity,
+ max_blocked_streams, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
+ decoder->opcode = 0;
+ decoder->written_icnt = 0;
+ decoder->max_concurrent_streams = 0;
+
+ nghttp3_qpack_read_state_reset(&decoder->rstate);
+ nghttp3_buf_init(&decoder->dbuf);
+
+ return 0;
+}
+
+void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder) {
+ nghttp3_buf_free(&decoder->dbuf, decoder->ctx.mem);
+ nghttp3_qpack_read_state_free(&decoder->rstate);
+ qpack_context_free(&decoder->ctx);
+}
+
+/*
+ * qpack_read_huffman_string decodes huffman string in buffer [begin,
+ * end) and writes the decoded string to |dest|. This function
+ * assumes the buffer pointed by |dest| has enough space.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_FATAL
+ * Could not decode huffman string.
+ */
+static nghttp3_ssize qpack_read_huffman_string(nghttp3_qpack_read_state *rstate,
+ nghttp3_buf *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ nghttp3_ssize nwrite;
+ size_t len = (size_t)(end - begin);
+ int fin = 0;
+
+ if (len >= rstate->left) {
+ len = (size_t)rstate->left;
+ fin = 1;
+ }
+
+ nwrite = nghttp3_qpack_huffman_decode(&rstate->huffman_ctx, dest->last, begin,
+ len, fin);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ if (nghttp3_qpack_huffman_decode_failure_state(&rstate->huffman_ctx)) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ dest->last += nwrite;
+ rstate->left -= len;
+ return (nghttp3_ssize)len;
+}
+
+static nghttp3_ssize qpack_read_string(nghttp3_qpack_read_state *rstate,
+ nghttp3_buf *dest, const uint8_t *begin,
+ const uint8_t *end) {
+ size_t len = (size_t)(end - begin);
+ size_t n = (size_t)nghttp3_min((uint64_t)len, rstate->left);
+
+ dest->last = nghttp3_cpymem(dest->last, begin, n);
+
+ rstate->left -= n;
+ return (nghttp3_ssize)n;
+}
+
+/*
+ * qpack_decoder_validate_index checks rstate->absidx is acceptable.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_QPACK_FATAL
+ * rstate->absidx is invalid.
+ */
+static int qpack_decoder_validate_index(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_read_state *rstate) {
+ if (rstate->dynamic) {
+ return rstate->absidx < decoder->ctx.next_absidx &&
+ decoder->ctx.next_absidx - rstate->absidx - 1 <
+ nghttp3_ringbuf_len(&decoder->ctx.dtable)
+ ? 0
+ : NGHTTP3_ERR_QPACK_FATAL;
+ }
+ return rstate->absidx < nghttp3_arraylen(stable) ? 0
+ : NGHTTP3_ERR_QPACK_FATAL;
+}
+
+static void qpack_read_state_check_huffman(nghttp3_qpack_read_state *rstate,
+ const uint8_t b) {
+ rstate->huffman_encoded = (b & (1 << rstate->prefix)) != 0;
+}
+
+static void qpack_read_state_terminate_name(nghttp3_qpack_read_state *rstate) {
+ *rstate->namebuf.last = '\0';
+ rstate->name->len = nghttp3_buf_len(&rstate->namebuf);
+}
+
+static void qpack_read_state_terminate_value(nghttp3_qpack_read_state *rstate) {
+ *rstate->valuebuf.last = '\0';
+ rstate->value->len = nghttp3_buf_len(&rstate->valuebuf);
+}
+
+nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder,
+ const uint8_t *src,
+ size_t srclen) {
+ const uint8_t *p = src, *end;
+ int rv;
+ int busy = 0;
+ const nghttp3_mem *mem = decoder->ctx.mem;
+ nghttp3_ssize nread;
+ int rfin;
+
+ if (decoder->ctx.bad) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ if (srclen == 0) {
+ return 0;
+ }
+
+ end = src + srclen;
+
+ for (; p != end || busy;) {
+ busy = 0;
+ switch (decoder->state) {
+ case NGHTTP3_QPACK_ES_STATE_OPCODE:
+ if ((*p) & 0x80) {
+ DEBUGF("qpack::decode: OPCODE_INSERT_INDEXED\n");
+ decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED;
+ decoder->rstate.dynamic = !((*p) & 0x40);
+ decoder->rstate.prefix = 6;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX;
+ } else if ((*p) & 0x40) {
+ DEBUGF("qpack::decode: OPCODE_INSERT\n");
+ decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_INSERT;
+ decoder->rstate.dynamic = 0;
+ decoder->rstate.prefix = 5;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN;
+ } else if ((*p) & 0x20) {
+ DEBUGF("qpack::decode: OPCODE_SET_DTABLE_TABLE_CAP\n");
+ decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP;
+ decoder->rstate.prefix = 5;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX;
+ } else if (!((*p) & 0x20)) {
+ DEBUGF("qpack::decode: OPCODE_DUPLICATE\n");
+ decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_DUPLICATE;
+ decoder->rstate.dynamic = 1;
+ decoder->rstate.prefix = 5;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX;
+ } else {
+ DEBUGF("qpack::decode: unknown opcode %02x\n", *p);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_INDEX:
+ nread = qpack_read_varint(&rfin, &decoder->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ return p - src;
+ }
+
+ if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP) {
+ DEBUGF("qpack::decode: Set dtable capacity to %" PRIu64 "\n",
+ decoder->rstate.left);
+ rv = nghttp3_qpack_decoder_set_max_dtable_capacity(
+ decoder, (size_t)decoder->rstate.left);
+ if (rv != 0) {
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&decoder->rstate);
+ break;
+ }
+
+ rv = nghttp3_qpack_decoder_rel2abs(decoder, &decoder->rstate);
+ if (rv < 0) {
+ goto fail;
+ }
+
+ switch (decoder->opcode) {
+ case NGHTTP3_QPACK_ES_OPCODE_DUPLICATE:
+ rv = nghttp3_qpack_decoder_dtable_duplicate_add(decoder);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&decoder->rstate);
+
+ break;
+ case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED:
+ decoder->rstate.prefix = 7;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN;
+
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ break;
+ case NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN:
+ qpack_read_state_check_huffman(&decoder->rstate, *p);
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAMELEN;
+ decoder->rstate.left = 0;
+ decoder->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_ES_STATE_READ_NAMELEN:
+ nread = qpack_read_varint(&rfin, &decoder->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ return p - src;
+ }
+
+ if (decoder->rstate.left > NGHTTP3_QPACK_MAX_NAMELEN) {
+ rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE;
+ goto fail;
+ }
+
+ if (decoder->rstate.huffman_encoded) {
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN;
+ nghttp3_qpack_huffman_decode_context_init(&decoder->rstate.huffman_ctx);
+ rv = nghttp3_rcbuf_new(&decoder->rstate.name,
+ (size_t)decoder->rstate.left * 2 + 1, mem);
+ } else {
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME;
+ rv = nghttp3_rcbuf_new(&decoder->rstate.name,
+ (size_t)decoder->rstate.left + 1, mem);
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_buf_wrap_init(&decoder->rstate.namebuf,
+ decoder->rstate.name->base,
+ decoder->rstate.name->len);
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN:
+ nread = qpack_read_huffman_string(&decoder->rstate,
+ &decoder->rstate.namebuf, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (decoder->rstate.left) {
+ return p - src;
+ }
+
+ qpack_read_state_terminate_name(&decoder->rstate);
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN;
+ decoder->rstate.prefix = 7;
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_NAME:
+ nread =
+ qpack_read_string(&decoder->rstate, &decoder->rstate.namebuf, p, end);
+ if (nread < 0) {
+ rv = (int)nread;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (decoder->rstate.left) {
+ return p - src;
+ }
+
+ qpack_read_state_terminate_name(&decoder->rstate);
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN;
+ decoder->rstate.prefix = 7;
+ break;
+ case NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN:
+ qpack_read_state_check_huffman(&decoder->rstate, *p);
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUELEN;
+ decoder->rstate.left = 0;
+ decoder->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_ES_STATE_READ_VALUELEN:
+ nread = qpack_read_varint(&rfin, &decoder->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ return p - src;
+ }
+
+ if (decoder->rstate.left > NGHTTP3_QPACK_MAX_VALUELEN) {
+ rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE;
+ goto fail;
+ }
+
+ if (decoder->rstate.huffman_encoded) {
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN;
+ nghttp3_qpack_huffman_decode_context_init(&decoder->rstate.huffman_ctx);
+ rv = nghttp3_rcbuf_new(&decoder->rstate.value,
+ (size_t)decoder->rstate.left * 2 + 1, mem);
+ } else {
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE;
+ rv = nghttp3_rcbuf_new(&decoder->rstate.value,
+ (size_t)decoder->rstate.left + 1, mem);
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_buf_wrap_init(&decoder->rstate.valuebuf,
+ decoder->rstate.value->base,
+ decoder->rstate.value->len);
+
+ /* value might be 0 length */
+ busy = 1;
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN:
+ nread = qpack_read_huffman_string(&decoder->rstate,
+ &decoder->rstate.valuebuf, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (decoder->rstate.left) {
+ return p - src;
+ }
+
+ qpack_read_state_terminate_value(&decoder->rstate);
+
+ switch (decoder->opcode) {
+ case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED:
+ rv = nghttp3_qpack_decoder_dtable_indexed_add(decoder);
+ break;
+ case NGHTTP3_QPACK_ES_OPCODE_INSERT:
+ rv = nghttp3_qpack_decoder_dtable_literal_add(decoder);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&decoder->rstate);
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_VALUE:
+ nread = qpack_read_string(&decoder->rstate, &decoder->rstate.valuebuf, p,
+ end);
+ if (nread < 0) {
+ rv = (int)nread;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (decoder->rstate.left) {
+ return p - src;
+ }
+
+ qpack_read_state_terminate_value(&decoder->rstate);
+
+ switch (decoder->opcode) {
+ case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED:
+ rv = nghttp3_qpack_decoder_dtable_indexed_add(decoder);
+ break;
+ case NGHTTP3_QPACK_ES_OPCODE_INSERT:
+ rv = nghttp3_qpack_decoder_dtable_literal_add(decoder);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&decoder->rstate);
+ break;
+ }
+ }
+
+ return p - src;
+
+fail:
+ decoder->ctx.bad = 1;
+ return rv;
+}
+
+int nghttp3_qpack_decoder_set_max_dtable_capacity(
+ nghttp3_qpack_decoder *decoder, size_t max_dtable_capacity) {
+ nghttp3_qpack_entry *ent;
+ size_t i;
+ nghttp3_qpack_context *ctx = &decoder->ctx;
+ const nghttp3_mem *mem = ctx->mem;
+
+ if (max_dtable_capacity > decoder->ctx.hard_max_dtable_capacity) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ ctx->max_dtable_capacity = max_dtable_capacity;
+
+ while (ctx->dtable_size > max_dtable_capacity) {
+ i = nghttp3_ringbuf_len(&ctx->dtable);
+ assert(i);
+ ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1);
+
+ ctx->dtable_size -= table_space(ent->nv.name->len, ent->nv.value->len);
+
+ nghttp3_ringbuf_pop_back(&ctx->dtable);
+ nghttp3_qpack_entry_free(ent);
+ nghttp3_mem_free(mem, ent);
+ }
+
+ return 0;
+}
+
+int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder) {
+ DEBUGF("qpack::decode: Insert With Name Reference (%s) absidx=%" PRIu64 ": "
+ "value=%*s\n",
+ decoder->rstate.dynamic ? "dynamic" : "static", decoder->rstate.absidx,
+ (int)decoder->rstate.value->len, decoder->rstate.value->base);
+
+ if (decoder->rstate.dynamic) {
+ return nghttp3_qpack_decoder_dtable_dynamic_add(decoder);
+ }
+
+ return nghttp3_qpack_decoder_dtable_static_add(decoder);
+}
+
+int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder) {
+ nghttp3_qpack_nv qnv;
+ int rv;
+ const nghttp3_qpack_static_header *shd;
+
+ shd = &stable[decoder->rstate.absidx];
+
+ if (table_space(shd->name.len, decoder->rstate.value->len) >
+ decoder->ctx.max_dtable_capacity) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+
+ qnv.name = (nghttp3_rcbuf *)&shd->name;
+ qnv.value = decoder->rstate.value;
+ qnv.token = shd->token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
+
+ nghttp3_rcbuf_decref(qnv.value);
+
+ return rv;
+}
+
+int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder) {
+ nghttp3_qpack_nv qnv;
+ int rv;
+ nghttp3_qpack_entry *ent;
+
+ ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx);
+
+ if (table_space(ent->nv.name->len, decoder->rstate.value->len) >
+ decoder->ctx.max_dtable_capacity) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+
+ qnv.name = ent->nv.name;
+ qnv.value = decoder->rstate.value;
+ qnv.token = ent->nv.token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ nghttp3_rcbuf_incref(qnv.name);
+
+ rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder) {
+ int rv;
+ nghttp3_qpack_entry *ent;
+ nghttp3_qpack_nv qnv;
+
+ DEBUGF("qpack::decode: Insert duplicate absidx=%" PRIu64 "\n",
+ decoder->rstate.absidx);
+
+ ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx);
+
+ if (table_space(ent->nv.name->len, ent->nv.value->len) >
+ decoder->ctx.max_dtable_capacity) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+
+ qnv = ent->nv;
+ nghttp3_rcbuf_incref(qnv.name);
+ nghttp3_rcbuf_incref(qnv.value);
+
+ rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder) {
+ nghttp3_qpack_nv qnv;
+ int rv;
+
+ DEBUGF("qpack::decode: Insert With Literal Name: name=%*s value=%*s\n",
+ (int)decoder->rstate.name->len, decoder->rstate.name->base,
+ (int)decoder->rstate.value->len, decoder->rstate.value->base);
+
+ if (table_space(decoder->rstate.name->len, decoder->rstate.value->len) >
+ decoder->ctx.max_dtable_capacity) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+
+ qnv.name = decoder->rstate.name;
+ qnv.value = decoder->rstate.value;
+ qnv.token = qpack_lookup_token(qnv.name->base, qnv.name->len);
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+void nghttp3_qpack_decoder_set_max_concurrent_streams(
+ nghttp3_qpack_decoder *decoder, size_t max_concurrent_streams) {
+ decoder->max_concurrent_streams =
+ nghttp3_max(decoder->max_concurrent_streams, max_concurrent_streams);
+}
+
+void nghttp3_qpack_stream_context_init(nghttp3_qpack_stream_context *sctx,
+ int64_t stream_id,
+ const nghttp3_mem *mem) {
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ sctx->mem = mem;
+ sctx->rstate.prefix = 8;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_RICNT;
+ sctx->opcode = 0;
+ sctx->stream_id = stream_id;
+ sctx->ricnt = 0;
+ sctx->dbase_sign = 0;
+ sctx->base = 0;
+}
+
+void nghttp3_qpack_stream_context_free(nghttp3_qpack_stream_context *sctx) {
+ nghttp3_qpack_read_state_free(&sctx->rstate);
+}
+
+void nghttp3_qpack_stream_context_reset(nghttp3_qpack_stream_context *sctx) {
+ nghttp3_qpack_stream_context_init(sctx, sctx->stream_id, sctx->mem);
+}
+
+uint64_t
+nghttp3_qpack_stream_context_get_ricnt(nghttp3_qpack_stream_context *sctx) {
+ return sctx->ricnt;
+}
+
+nghttp3_ssize
+nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv, uint8_t *pflags,
+ const uint8_t *src, size_t srclen, int fin) {
+ const uint8_t *p = src, *end = src ? src + srclen : src;
+ int rv;
+ int busy = 0;
+ nghttp3_ssize nread;
+ int rfin;
+ const nghttp3_mem *mem = decoder->ctx.mem;
+
+ if (decoder->ctx.bad) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ *pflags = NGHTTP3_QPACK_DECODE_FLAG_NONE;
+
+ for (; p != end || busy;) {
+ busy = 0;
+ switch (sctx->state) {
+ case NGHTTP3_QPACK_RS_STATE_RICNT:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ rv = nghttp3_qpack_decoder_reconstruct_ricnt(decoder, &sctx->ricnt,
+ sctx->rstate.left);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_DBASE_SIGN;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_DBASE_SIGN:
+ if ((*p) & 0x80) {
+ sctx->dbase_sign = 1;
+ }
+ sctx->state = NGHTTP3_QPACK_RS_STATE_DBASE;
+ sctx->rstate.left = 0;
+ sctx->rstate.prefix = 7;
+ sctx->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_RS_STATE_DBASE:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ if (sctx->dbase_sign) {
+ if (sctx->ricnt <= sctx->rstate.left) {
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+ sctx->base = sctx->ricnt - sctx->rstate.left - 1;
+ } else {
+ sctx->base = sctx->ricnt + sctx->rstate.left;
+ }
+
+ DEBUGF("qpack::decode: ricnt=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64
+ "\n",
+ sctx->ricnt, sctx->base, decoder->ctx.next_absidx);
+
+ if (sctx->ricnt > decoder->ctx.next_absidx) {
+ DEBUGF("qpack::decode: stream blocked\n");
+ sctx->state = NGHTTP3_QPACK_RS_STATE_BLOCKED;
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_BLOCKED;
+ return p - src;
+ }
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ sctx->rstate.left = 0;
+ sctx->rstate.shift = 0;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_OPCODE:
+ assert(sctx->rstate.left == 0);
+ assert(sctx->rstate.shift == 0);
+ if ((*p) & 0x80) {
+ DEBUGF("qpack::decode: OPCODE_INDEXED\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED;
+ sctx->rstate.dynamic = !((*p) & 0x40);
+ sctx->rstate.prefix = 6;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX;
+ } else if ((*p) & 0x40) {
+ DEBUGF("qpack::decode: OPCODE_INDEXED_NAME\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME;
+ sctx->rstate.never = (*p) & 0x20;
+ sctx->rstate.dynamic = !((*p) & 0x10);
+ sctx->rstate.prefix = 4;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX;
+ } else if ((*p) & 0x20) {
+ DEBUGF("qpack::decode: OPCODE_LITERAL\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_LITERAL;
+ sctx->rstate.never = (*p) & 0x10;
+ sctx->rstate.dynamic = 0;
+ sctx->rstate.prefix = 3;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN;
+ } else if ((*p) & 0x10) {
+ DEBUGF("qpack::decode: OPCODE_INDEXED_PB\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB;
+ sctx->rstate.dynamic = 1;
+ sctx->rstate.prefix = 4;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX;
+ } else {
+ DEBUGF("qpack::decode: OPCODE_INDEXED_NAME_PB\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB;
+ sctx->rstate.never = (*p) & 0x08;
+ sctx->rstate.dynamic = 1;
+ sctx->rstate.prefix = 3;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX;
+ }
+ break;
+ case NGHTTP3_QPACK_RS_STATE_READ_INDEX:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ switch (sctx->opcode) {
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED:
+ rv = nghttp3_qpack_decoder_brel2abs(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_qpack_decoder_emit_indexed(decoder, sctx, nv);
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT;
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ return p - src;
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB:
+ rv = nghttp3_qpack_decoder_pbrel2abs(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_qpack_decoder_emit_indexed(decoder, sctx, nv);
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT;
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ return p - src;
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME:
+ rv = nghttp3_qpack_decoder_brel2abs(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ sctx->rstate.prefix = 7;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN;
+ break;
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB:
+ rv = nghttp3_qpack_decoder_pbrel2abs(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ sctx->rstate.prefix = 7;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN;
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+ break;
+ case NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN:
+ qpack_read_state_check_huffman(&sctx->rstate, *p);
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAMELEN;
+ sctx->rstate.left = 0;
+ sctx->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_RS_STATE_READ_NAMELEN:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ if (sctx->rstate.left > NGHTTP3_QPACK_MAX_NAMELEN) {
+ rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE;
+ goto fail;
+ }
+
+ if (sctx->rstate.huffman_encoded) {
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN;
+ nghttp3_qpack_huffman_decode_context_init(&sctx->rstate.huffman_ctx);
+ rv = nghttp3_rcbuf_new(&sctx->rstate.name,
+ (size_t)sctx->rstate.left * 2 + 1, mem);
+ } else {
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME;
+ rv = nghttp3_rcbuf_new(&sctx->rstate.name,
+ (size_t)sctx->rstate.left + 1, mem);
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_buf_wrap_init(&sctx->rstate.namebuf, sctx->rstate.name->base,
+ sctx->rstate.name->len);
+ break;
+ case NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN:
+ nread = qpack_read_huffman_string(&sctx->rstate, &sctx->rstate.namebuf, p,
+ end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (sctx->rstate.left) {
+ goto almost_ok;
+ }
+
+ qpack_read_state_terminate_name(&sctx->rstate);
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN;
+ sctx->rstate.prefix = 7;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_READ_NAME:
+ nread = qpack_read_string(&sctx->rstate, &sctx->rstate.namebuf, p, end);
+ if (nread < 0) {
+ rv = (int)nread;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (sctx->rstate.left) {
+ goto almost_ok;
+ }
+
+ qpack_read_state_terminate_name(&sctx->rstate);
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN;
+ sctx->rstate.prefix = 7;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN:
+ qpack_read_state_check_huffman(&sctx->rstate, *p);
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUELEN;
+ sctx->rstate.left = 0;
+ sctx->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_RS_STATE_READ_VALUELEN:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ if (sctx->rstate.left > NGHTTP3_QPACK_MAX_VALUELEN) {
+ rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE;
+ goto fail;
+ }
+
+ if (sctx->rstate.huffman_encoded) {
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN;
+ nghttp3_qpack_huffman_decode_context_init(&sctx->rstate.huffman_ctx);
+ rv = nghttp3_rcbuf_new(&sctx->rstate.value,
+ (size_t)sctx->rstate.left * 2 + 1, mem);
+ } else {
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE;
+ rv = nghttp3_rcbuf_new(&sctx->rstate.value,
+ (size_t)sctx->rstate.left + 1, mem);
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_buf_wrap_init(&sctx->rstate.valuebuf, sctx->rstate.value->base,
+ sctx->rstate.value->len);
+
+ /* value might be 0 length */
+ busy = 1;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN:
+ nread = qpack_read_huffman_string(&sctx->rstate, &sctx->rstate.valuebuf,
+ p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (sctx->rstate.left) {
+ goto almost_ok;
+ }
+
+ qpack_read_state_terminate_value(&sctx->rstate);
+
+ switch (sctx->opcode) {
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME:
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB:
+ rv = nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ break;
+ case NGHTTP3_QPACK_RS_OPCODE_LITERAL:
+ nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT;
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ return p - src;
+ case NGHTTP3_QPACK_RS_STATE_READ_VALUE:
+ nread = qpack_read_string(&sctx->rstate, &sctx->rstate.valuebuf, p, end);
+ if (nread < 0) {
+ rv = (int)nread;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (sctx->rstate.left) {
+ goto almost_ok;
+ }
+
+ qpack_read_state_terminate_value(&sctx->rstate);
+
+ switch (sctx->opcode) {
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME:
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB:
+ rv = nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ break;
+ case NGHTTP3_QPACK_RS_OPCODE_LITERAL:
+ nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT;
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ return p - src;
+ case NGHTTP3_QPACK_RS_STATE_BLOCKED:
+ if (sctx->ricnt > decoder->ctx.next_absidx) {
+ DEBUGF("qpack::decode: stream still blocked\n");
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_BLOCKED;
+ return p - src;
+ }
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+ break;
+ }
+ }
+
+almost_ok:
+ if (fin) {
+ if (sctx->state != NGHTTP3_QPACK_RS_STATE_OPCODE) {
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_FINAL;
+
+ if (sctx->ricnt) {
+ rv = nghttp3_qpack_decoder_write_section_ack(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+ }
+
+ return p - src;
+
+fail:
+ decoder->ctx.bad = 1;
+ return rv;
+}
+
+static int qpack_decoder_dbuf_overflow(nghttp3_qpack_decoder *decoder) {
+ size_t limit = nghttp3_max(decoder->max_concurrent_streams, 100);
+ /* 10 = nghttp3_qpack_put_varint_len((1ULL << 62) - 1, 2)) */
+ return nghttp3_buf_len(&decoder->dbuf) > limit * 2 * 10;
+}
+
+int nghttp3_qpack_decoder_write_section_ack(
+ nghttp3_qpack_decoder *decoder, const nghttp3_qpack_stream_context *sctx) {
+ nghttp3_buf *dbuf = &decoder->dbuf;
+ uint8_t *p;
+ int rv;
+
+ if (qpack_decoder_dbuf_overflow(decoder)) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ rv = reserve_buf_small(
+ dbuf, nghttp3_qpack_put_varint_len((uint64_t)sctx->stream_id, 7),
+ decoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = dbuf->last;
+ *p = 0x80;
+ dbuf->last = nghttp3_qpack_put_varint(p, (uint64_t)sctx->stream_id, 7);
+
+ if (decoder->written_icnt < sctx->ricnt) {
+ decoder->written_icnt = sctx->ricnt;
+ }
+
+ return 0;
+}
+
+size_t
+nghttp3_qpack_decoder_get_decoder_streamlen(nghttp3_qpack_decoder *decoder) {
+ uint64_t n;
+ size_t len = 0;
+
+ if (decoder->written_icnt < decoder->ctx.next_absidx) {
+ n = decoder->ctx.next_absidx - decoder->written_icnt;
+ len = nghttp3_qpack_put_varint_len(n, 6);
+ }
+
+ return nghttp3_buf_len(&decoder->dbuf) + len;
+}
+
+void nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder,
+ nghttp3_buf *dbuf) {
+ uint8_t *p;
+ uint64_t n = 0;
+ size_t len = 0;
+ (void)len;
+
+ if (decoder->written_icnt < decoder->ctx.next_absidx) {
+ n = decoder->ctx.next_absidx - decoder->written_icnt;
+ len = nghttp3_qpack_put_varint_len(n, 6);
+ }
+
+ assert(nghttp3_buf_left(dbuf) >= nghttp3_buf_len(&decoder->dbuf) + len);
+
+ if (nghttp3_buf_len(&decoder->dbuf)) {
+ dbuf->last = nghttp3_cpymem(dbuf->last, decoder->dbuf.pos,
+ nghttp3_buf_len(&decoder->dbuf));
+ }
+
+ if (n) {
+ p = dbuf->last;
+ *p = 0;
+ dbuf->last = nghttp3_qpack_put_varint(p, n, 6);
+
+ decoder->written_icnt = decoder->ctx.next_absidx;
+ }
+
+ nghttp3_buf_reset(&decoder->dbuf);
+}
+
+int nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder,
+ int64_t stream_id) {
+ uint8_t *p;
+ int rv;
+
+ if (qpack_decoder_dbuf_overflow(decoder)) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ rv = reserve_buf(&decoder->dbuf,
+ nghttp3_qpack_put_varint_len((uint64_t)stream_id, 6),
+ decoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = decoder->dbuf.last;
+ *p = 0x40;
+ decoder->dbuf.last = nghttp3_qpack_put_varint(p, (uint64_t)stream_id, 6);
+
+ return 0;
+}
+
+int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder,
+ uint64_t *dest, uint64_t encricnt) {
+ uint64_t max_ents, full, max, max_wrapped, ricnt;
+
+ if (encricnt == 0) {
+ *dest = 0;
+ return 0;
+ }
+
+ max_ents =
+ decoder->ctx.hard_max_dtable_capacity / NGHTTP3_QPACK_ENTRY_OVERHEAD;
+ full = 2 * max_ents;
+
+ if (encricnt > full) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ max = decoder->ctx.next_absidx + max_ents;
+ max_wrapped = max / full * full;
+ ricnt = max_wrapped + encricnt - 1;
+
+ if (ricnt > max) {
+ if (ricnt <= full) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ ricnt -= full;
+ }
+
+ if (ricnt == 0) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ *dest = ricnt;
+
+ return 0;
+}
+
+int nghttp3_qpack_decoder_rel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_read_state *rstate) {
+ DEBUGF("qpack::decode: dynamic=%d relidx=%" PRIu64 " icnt=%" PRIu64 "\n",
+ rstate->dynamic, rstate->left, decoder->ctx.next_absidx);
+
+ if (rstate->dynamic) {
+ if (decoder->ctx.next_absidx < rstate->left + 1) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+ rstate->absidx = decoder->ctx.next_absidx - rstate->left - 1;
+ } else {
+ rstate->absidx = rstate->left;
+ }
+ if (qpack_decoder_validate_index(decoder, rstate) != 0) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+ return 0;
+}
+
+int nghttp3_qpack_decoder_brel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx) {
+ nghttp3_qpack_read_state *rstate = &sctx->rstate;
+
+ DEBUGF("qpack::decode: dynamic=%d relidx=%" PRIu64 " base=%" PRIu64
+ " icnt=%" PRIu64 "\n",
+ rstate->dynamic, rstate->left, sctx->base, decoder->ctx.next_absidx);
+
+ if (rstate->dynamic) {
+ if (sctx->base < rstate->left + 1) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ rstate->absidx = sctx->base - rstate->left - 1;
+
+ if (rstate->absidx >= sctx->ricnt) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ } else {
+ rstate->absidx = rstate->left;
+ }
+
+ if (qpack_decoder_validate_index(decoder, rstate) != 0) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ return 0;
+}
+
+int nghttp3_qpack_decoder_pbrel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx) {
+ nghttp3_qpack_read_state *rstate = &sctx->rstate;
+
+ DEBUGF("qpack::decode: pbidx=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 "\n",
+ rstate->left, sctx->base, decoder->ctx.next_absidx);
+
+ assert(rstate->dynamic);
+
+ rstate->absidx = rstate->left + sctx->base;
+
+ if (rstate->absidx >= sctx->ricnt) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ if (qpack_decoder_validate_index(decoder, rstate) != 0) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ return 0;
+}
+
+static void
+qpack_decoder_emit_static_indexed(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ const nghttp3_qpack_static_header *shd = &stable[sctx->rstate.absidx];
+ (void)decoder;
+
+ nv->name = (nghttp3_rcbuf *)&shd->name;
+ nv->value = (nghttp3_rcbuf *)&shd->value;
+ nv->token = shd->token;
+ nv->flags = NGHTTP3_NV_FLAG_NONE;
+}
+
+static void
+qpack_decoder_emit_dynamic_indexed(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ nghttp3_qpack_entry *ent =
+ nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx);
+
+ *nv = ent->nv;
+
+ nghttp3_rcbuf_incref(nv->name);
+ nghttp3_rcbuf_incref(nv->value);
+}
+
+void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ DEBUGF("qpack::decode: Indexed (%s) absidx=%" PRIu64 "\n",
+ sctx->rstate.dynamic ? "dynamic" : "static", sctx->rstate.absidx);
+
+ if (sctx->rstate.dynamic) {
+ qpack_decoder_emit_dynamic_indexed(decoder, sctx, nv);
+ } else {
+ qpack_decoder_emit_static_indexed(decoder, sctx, nv);
+ }
+}
+
+static void
+qpack_decoder_emit_static_indexed_name(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ const nghttp3_qpack_static_header *shd = &stable[sctx->rstate.absidx];
+ (void)decoder;
+
+ nv->name = (nghttp3_rcbuf *)&shd->name;
+ nv->value = sctx->rstate.value;
+ nv->token = shd->token;
+ nv->flags =
+ sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE;
+
+ sctx->rstate.value = NULL;
+}
+
+static int
+qpack_decoder_emit_dynamic_indexed_name(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ nghttp3_qpack_entry *ent;
+
+ /* A broken encoder might change dtable capacity while processing
+ request stream instruction. Check the absidx again. */
+ if (qpack_decoder_validate_index(decoder, &sctx->rstate) != 0) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx);
+
+ nv->name = ent->nv.name;
+ nv->value = sctx->rstate.value;
+ nv->token = ent->nv.token;
+ nv->flags =
+ sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE;
+
+ nghttp3_rcbuf_incref(nv->name);
+
+ sctx->rstate.value = NULL;
+
+ return 0;
+}
+
+int nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ (void)decoder;
+
+ DEBUGF("qpack::decode: Indexed name (%s) absidx=%" PRIu64 " value=%*s\n",
+ sctx->rstate.dynamic ? "dynamic" : "static", sctx->rstate.absidx,
+ (int)sctx->rstate.value->len, sctx->rstate.value->base);
+
+ if (sctx->rstate.dynamic) {
+ return qpack_decoder_emit_dynamic_indexed_name(decoder, sctx, nv);
+ }
+
+ qpack_decoder_emit_static_indexed_name(decoder, sctx, nv);
+
+ return 0;
+}
+
+void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ (void)decoder;
+
+ DEBUGF("qpack::decode: Emit literal name=%*s value=%*s\n",
+ (int)sctx->rstate.name->len, sctx->rstate.name->base,
+ (int)sctx->rstate.value->len, sctx->rstate.value->base);
+
+ nv->name = sctx->rstate.name;
+ nv->value = sctx->rstate.value;
+ nv->token = qpack_lookup_token(nv->name->base, nv->name->len);
+ nv->flags =
+ sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE;
+
+ sctx->rstate.name = NULL;
+ sctx->rstate.value = NULL;
+}
+
+int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
+ size_t hard_max_dtable_capacity,
+ const nghttp3_mem *mem) {
+ int rv;
+ nghttp3_qpack_encoder *p;
+
+ p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_encoder));
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ rv = nghttp3_qpack_encoder_init(p, hard_max_dtable_capacity, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ *pencoder = p;
+
+ return 0;
+}
+
+void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder) {
+ const nghttp3_mem *mem;
+
+ if (encoder == NULL) {
+ return;
+ }
+
+ mem = encoder->ctx.mem;
+
+ nghttp3_qpack_encoder_free(encoder);
+ nghttp3_mem_free(mem, encoder);
+}
+
+int nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx,
+ int64_t stream_id,
+ const nghttp3_mem *mem) {
+ nghttp3_qpack_stream_context *p;
+
+ p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream_context));
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ nghttp3_qpack_stream_context_init(p, stream_id, mem);
+
+ *psctx = p;
+
+ return 0;
+}
+
+void nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx) {
+ const nghttp3_mem *mem;
+
+ if (sctx == NULL) {
+ return;
+ }
+
+ mem = sctx->mem;
+
+ nghttp3_qpack_stream_context_free(sctx);
+ nghttp3_mem_free(mem, sctx);
+}
+
+int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
+ const nghttp3_mem *mem) {
+ int rv;
+ nghttp3_qpack_decoder *p;
+
+ p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_decoder));
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ rv = nghttp3_qpack_decoder_init(p, hard_max_dtable_capacity,
+ max_blocked_streams, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ *pdecoder = p;
+
+ return 0;
+}
+
+void nghttp3_qpack_decoder_del(nghttp3_qpack_decoder *decoder) {
+ const nghttp3_mem *mem;
+
+ if (decoder == NULL) {
+ return;
+ }
+
+ mem = decoder->ctx.mem;
+
+ nghttp3_qpack_decoder_free(decoder);
+ nghttp3_mem_free(mem, decoder);
+}
+
+uint64_t nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder) {
+ return decoder->ctx.next_absidx;
+}
diff --git a/lib/nghttp3_qpack.h b/lib/nghttp3_qpack.h
new file mode 100644
index 0000000..804969e
--- /dev/null
+++ b/lib/nghttp3_qpack.h
@@ -0,0 +1,996 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_QPACK_H
+#define NGHTTP3_QPACK_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_rcbuf.h"
+#include "nghttp3_map.h"
+#include "nghttp3_pq.h"
+#include "nghttp3_ringbuf.h"
+#include "nghttp3_buf.h"
+#include "nghttp3_ksl.h"
+#include "nghttp3_qpack_huffman.h"
+
+#define NGHTTP3_QPACK_INT_MAX ((1ull << 62) - 1)
+
+/* NGHTTP3_QPACK_MAX_NAMELEN is the maximum (compressed) length of
+ header name this library can decode. */
+#define NGHTTP3_QPACK_MAX_NAMELEN 256
+/* NGHTTP3_QPACK_MAX_VALUELEN is the maximum (compressed) length of
+ header value this library can decode. */
+#define NGHTTP3_QPACK_MAX_VALUELEN 65536
+
+/* nghttp3_qpack_indexing_mode is a indexing strategy. */
+typedef enum nghttp3_qpack_indexing_mode {
+ /* NGHTTP3_QPACK_INDEXING_MODE_LITERAL means that header field
+ should not be inserted into dynamic table. */
+ NGHTTP3_QPACK_INDEXING_MODE_LITERAL,
+ /* NGHTTP3_QPACK_INDEXING_MODE_STORE means that header field can be
+ inserted into dynamic table. */
+ NGHTTP3_QPACK_INDEXING_MODE_STORE,
+ /* NGHTTP3_QPACK_INDEXING_MODE_NEVER means that header field should
+ not be inserted into dynamic table and this must be true for all
+ forwarding paths. */
+ NGHTTP3_QPACK_INDEXING_MODE_NEVER,
+} nghttp3_qpack_indexing_mode;
+
+typedef struct nghttp3_qpack_entry nghttp3_qpack_entry;
+
+struct nghttp3_qpack_entry {
+ /* The header field name/value pair */
+ nghttp3_qpack_nv nv;
+ /* map_next points to the entry which shares same bucket in hash
+ table. */
+ nghttp3_qpack_entry *map_next;
+ /* sum is the sum of all entries inserted up to this entry. This
+ value does not contain the space required for this entry. */
+ size_t sum;
+ /* absidx is the absolute index of this entry. */
+ uint64_t absidx;
+ /* The hash value for header name (nv.name). */
+ uint32_t hash;
+};
+
+/* The entry used for static table. */
+typedef struct nghttp3_qpack_static_entry {
+ uint64_t absidx;
+ int32_t token;
+ uint32_t hash;
+} nghttp3_qpack_static_entry;
+
+typedef struct nghttp3_qpack_static_header {
+ nghttp3_rcbuf name;
+ nghttp3_rcbuf value;
+ int32_t token;
+} nghttp3_qpack_static_header;
+
+/*
+ * nghttp3_qpack_header_block_ref is created per encoded header block
+ * and includes the required insert count and the minimum insert count
+ * of dynamic table entry it refers to.
+ */
+typedef struct nghttp3_qpack_header_block_ref {
+ nghttp3_pq_entry max_cnts_pe;
+ nghttp3_pq_entry min_cnts_pe;
+ /* max_cnt is the required insert count. */
+ uint64_t max_cnt;
+ /* min_cnt is the minimum insert count of dynamic table entry it
+ refers to. In other words, this is the minimum absolute index of
+ dynamic header table entry this encoded block refers to plus
+ 1. */
+ uint64_t min_cnt;
+} nghttp3_qpack_header_block_ref;
+
+int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref,
+ uint64_t max_cnt, uint64_t min_cnt,
+ const nghttp3_mem *mem);
+
+void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref,
+ const nghttp3_mem *mem);
+
+typedef struct nghttp3_qpack_stream {
+ int64_t stream_id;
+ /* refs is an array of pointer to nghttp3_qpack_header_block_ref in
+ the order of the time they are encoded. HTTP/3 allows multiple
+ header blocks (e.g., non-final response headers, final response
+ headers, trailers, and push promises) per stream. */
+ nghttp3_ringbuf refs;
+ /* max_cnts is a priority queue sorted by descending order of
+ max_cnt of nghttp3_qpack_header_block_ref. */
+ nghttp3_pq max_cnts;
+} nghttp3_qpack_stream;
+
+int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id,
+ const nghttp3_mem *mem);
+
+void nghttp3_qpack_stream_del(nghttp3_qpack_stream *stream,
+ const nghttp3_mem *mem);
+
+uint64_t nghttp3_qpack_stream_get_max_cnt(const nghttp3_qpack_stream *stream);
+
+int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream,
+ nghttp3_qpack_header_block_ref *ref);
+
+void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream);
+
+#define NGHTTP3_QPACK_ENTRY_OVERHEAD 32
+
+typedef struct nghttp3_qpack_context {
+ /* dtable is a dynamic table */
+ nghttp3_ringbuf dtable;
+ /* mem is memory allocator */
+ const nghttp3_mem *mem;
+ /* dtable_size is abstracted buffer size of dtable as described in
+ the spec. This is the sum of length of name/value in dtable +
+ NGHTTP3_QPACK_ENTRY_OVERHEAD bytes overhead per each entry. */
+ size_t dtable_size;
+ size_t dtable_sum;
+ /* hard_max_dtable_capacity is the upper bound of
+ max_dtable_capacity. */
+ size_t hard_max_dtable_capacity;
+ /* max_dtable_capacity is the maximum capacity of the dynamic
+ table. */
+ size_t max_dtable_capacity;
+ /* max_blocked_streams is the maximum number of stream which can be
+ blocked. */
+ size_t max_blocked_streams;
+ /* next_absidx is the next absolute index for nghttp3_qpack_entry.
+ It is equivalent to insert count. */
+ uint64_t next_absidx;
+ /* If inflate/deflate error occurred, this value is set to 1 and
+ further invocation of inflate/deflate will fail with
+ NGHTTP3_ERR_QPACK_FATAL. */
+ uint8_t bad;
+} nghttp3_qpack_context;
+
+typedef struct nghttp3_qpack_read_state {
+ nghttp3_qpack_huffman_decode_context huffman_ctx;
+ nghttp3_buf namebuf;
+ nghttp3_buf valuebuf;
+ nghttp3_rcbuf *name;
+ nghttp3_rcbuf *value;
+ uint64_t left;
+ size_t prefix;
+ size_t shift;
+ uint64_t absidx;
+ int never;
+ int dynamic;
+ int huffman_encoded;
+} nghttp3_qpack_read_state;
+
+void nghttp3_qpack_read_state_free(nghttp3_qpack_read_state *rstate);
+
+void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate);
+
+#define NGHTTP3_QPACK_MAP_SIZE 64
+
+typedef struct nghttp3_qpack_map {
+ nghttp3_qpack_entry *table[NGHTTP3_QPACK_MAP_SIZE];
+} nghttp3_qpack_map;
+
+/* nghttp3_qpack_decoder_stream_state is a set of states when decoding
+ decoder stream. */
+typedef enum nghttp3_qpack_decoder_stream_state {
+ NGHTTP3_QPACK_DS_STATE_OPCODE,
+ NGHTTP3_QPACK_DS_STATE_READ_NUMBER,
+} nghttp3_qpack_decoder_stream_state;
+
+/* nghttp3_qpack_decoder_stream_opcode is opcode used in decoder
+ stream. */
+typedef enum nghttp3_qpack_decoder_stream_opcode {
+ NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT,
+ NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK,
+ NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL,
+} nghttp3_qpack_decoder_stream_opcode;
+
+/* QPACK encoder flags */
+
+/* NGHTTP3_QPACK_ENCODER_FLAG_NONE indicates that no flag is set. */
+#define NGHTTP3_QPACK_ENCODER_FLAG_NONE 0x00u
+/* NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP indicates that
+ Set Dynamic Table Capacity is required. */
+#define NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP 0x01u
+
+struct nghttp3_qpack_encoder {
+ nghttp3_qpack_context ctx;
+ /* dtable_map is a map of hash to nghttp3_qpack_entry to provide
+ fast access to an entry in dynamic table. */
+ nghttp3_qpack_map dtable_map;
+ /* streams is a map of stream ID to nghttp3_qpack_stream to keep
+ track of unacknowledged streams. */
+ nghttp3_map streams;
+ /* blocked_streams is an ordered list of nghttp3_qpack_stream, in
+ descending order of max_cnt, to search the unblocked streams by
+ received known count. */
+ nghttp3_ksl blocked_streams;
+ /* min_cnts is a priority queue of nghttp3_qpack_header_block_ref
+ sorted by ascending order of min_cnt to know that an entry can be
+ evicted from dynamic table. */
+ nghttp3_pq min_cnts;
+ /* krcnt is Known Received Count. */
+ uint64_t krcnt;
+ /* state is a current state of reading decoder stream. */
+ nghttp3_qpack_decoder_stream_state state;
+ /* opcode is a decoder stream opcode being processed. */
+ nghttp3_qpack_decoder_stream_opcode opcode;
+ /* rstate is a set of intermediate state which are used to process
+ decoder stream. */
+ nghttp3_qpack_read_state rstate;
+ /* min_dtable_update is the minimum dynamic table size required. */
+ size_t min_dtable_update;
+ /* last_max_dtable_update is the dynamic table size last
+ requested. */
+ size_t last_max_dtable_update;
+ /* flags is bitwise OR of zero or more of
+ NGHTTP3_QPACK_ENCODER_FLAG_*. */
+ uint8_t flags;
+};
+
+/*
+ * nghttp3_qpack_encoder_init initializes |encoder|.
+ * |hard_max_dtable_capacity| is the upper bound of the dynamic table
+ * capacity. |mem| is a memory allocator.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
+ size_t hard_max_dtable_capacity,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_qpack_encoder_free frees memory allocated for |encoder|.
+ * This function does not free memory pointed by |encoder|.
+ */
+void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder);
+
+/*
+ * nghttp3_qpack_encoder_encode_nv encodes |nv|. It writes request
+ * stream into |rbuf| and writes encoder stream into |ebuf|. |nv| is
+ * a header field to encode. |base| is base. |allow_blocking| is
+ * nonzero if this stream can be blocked (or it has been blocked
+ * already).
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder,
+ uint64_t *pmax_cnt, uint64_t *pmin_cnt,
+ nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ const nghttp3_nv *nv, uint64_t base,
+ int allow_blocking);
+
+/* nghttp3_qpack_lookup_result stores a result of table lookup. */
+typedef struct nghttp3_qpack_lookup_result {
+ /* index is an index of matched entry. -1 if no match is made. */
+ nghttp3_ssize index;
+ /* name_value_match is nonzero if both name and value are
+ matched. */
+ int name_value_match;
+ /* pb_index is the absolute index of matched post-based dynamic
+ table entry. -1 if no such entry exists. */
+ nghttp3_ssize pb_index;
+} nghttp3_qpack_lookup_result;
+
+/*
+ * nghttp3_qpack_lookup_stable searches |nv| in static table. |token|
+ * is a token of nv->name and it is -1 if there is no corresponding
+ * token defined. |indexing_mode| provides indexing strategy.
+ */
+nghttp3_qpack_lookup_result
+nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token,
+ nghttp3_qpack_indexing_mode indexing_mode);
+
+/*
+ * nghttp3_qpack_encoder_lookup_dtable searches |nv| in dynamic table.
+ * |token| is a token of nv->name and it is -1 if there is no
+ * corresponding token defined. |hash| is a hash of nv->name.
+ * |indexing_mode| provides indexing strategy. |krcnt| is Known
+ * Received Count. |allow_blocking| is nonzero if this stream can be
+ * blocked (or it has been blocked already).
+ */
+nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable(
+ nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token,
+ uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, uint64_t krcnt,
+ int allow_blocking);
+
+/*
+ * nghttp3_qpack_encoder_write_field_section_prefix writes Encoded
+ * Field Section Prefix into |pbuf|. |ricnt| is Required Insert
+ * Count. |base| is Base.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_field_section_prefix(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt,
+ uint64_t base);
+
+/*
+ * nghttp3_qpack_encoder_write_static_indexed writes Indexed Header
+ * Field to |rbuf|. |absidx| is an absolute index into static table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_static_indexed(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ uint64_t absidx);
+
+/*
+ * nghttp3_qpack_encoder_write_dynamic_indexed writes Indexed Header
+ * Field to |rbuf|. |absidx| is an absolute index into dynamic table.
+ * |base| is base.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_dynamic_indexed(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ uint64_t absidx, uint64_t base);
+
+/*
+ * nghttp3_qpack_encoder_write_static_indexed writes Literal Header
+ * Field With Name Reference to |rbuf|. |absidx| is an absolute index
+ * into static table to reference a name. |nv| is a header field to
+ * encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_static_indexed_name(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx,
+ const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_dynamic_indexed writes Literal Header
+ * Field With Name Reference to |rbuf|. |absidx| is an absolute index
+ * into dynamic table to reference a name. |base| is a base. |nv| is
+ * a header field to encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_dynamic_indexed_name(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx,
+ uint64_t base, const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_literal writes Literal Header Field
+ * With Literal Name to |rbuf|. |nv| is a header field to encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_static_insert writes Insert With Name
+ * Reference to |ebuf|. |absidx| is an absolute index into static
+ * table to reference a name. |nv| is a header field to insert.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_static_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx,
+ const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_dynamic_insert writes Insert With Name
+ * Reference to |ebuf|. |absidx| is an absolute index into dynamic
+ * table to reference a name. |nv| is a header field to insert.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_dynamic_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx,
+ const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_duplicate_insert writes Duplicate to
+ * |ebuf|. |absidx| is an absolute index into dynamic table to
+ * reference an entry.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_duplicate_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx);
+
+/*
+ * nghttp3_qpack_encoder_write_literal_insert writes Insert With
+ * Literal Name to |ebuf|. |nv| is a header field to insert.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_literal_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ const nghttp3_nv *nv);
+
+int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream);
+
+/*
+ * nghttp3_qpack_encoder_block_stream blocks |stream|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream);
+
+/*
+ * nghttp3_qpack_encoder_unblock_stream unblocks |stream|.
+ */
+void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream);
+
+/*
+ * nghttp3_qpack_encoder_unblock unblocks stream whose max_cnt is less
+ * than or equal to |max_cnt|.
+ */
+void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder,
+ uint64_t max_cnt);
+
+/*
+ * nghttp3_qpack_encoder_find_stream returns stream whose stream ID is
+ * |stream_id|. This function returns NULL if there is no such
+ * stream.
+ */
+nghttp3_qpack_stream *
+nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id);
+
+uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder);
+
+/*
+ * nghttp3_qpack_encoder_shrink_dtable shrinks dynamic table so that
+ * the dynamic table size is less than or equal to maximum size.
+ */
+void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder);
+
+/*
+ * nghttp3_qpack_encoder_process_dtable_update processes pending
+ * dynamic table size update. It might write encoder stream into
+ * |ebuf|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf);
+
+/*
+ * nghttp3_qpack_encoder_write_set_dtable_cap writes Set Dynamic Table
+ * Capacity. to |ebuf|. |cap| is the capacity of dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf, size_t cap);
+
+/*
+ * nghttp3_qpack_context_dtable_add adds |qnv| to dynamic table. If
+ * |ctx| is a part of encoder, |dtable_map| is not NULL. |hash| is a
+ * hash value of name.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx,
+ nghttp3_qpack_nv *qnv,
+ nghttp3_qpack_map *dtable_map,
+ uint32_t hash);
+
+/*
+ * nghttp3_qpack_encoder_dtable_static_add adds |nv| to dynamic table
+ * by referencing static table entry at an absolute index |absidx|.
+ * The hash of name is given as |hash|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_dtable_static_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ const nghttp3_nv *nv,
+ uint32_t hash);
+
+/*
+ * nghttp3_qpack_encoder_dtable_dynamic_add adds |nv| to dynamic table
+ * by referencing dynamic table entry at an absolute index |absidx|.
+ * The hash of name is given as |hash|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_dtable_dynamic_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ const nghttp3_nv *nv,
+ uint32_t hash);
+
+/*
+ * nghttp3_qpack_encoder_dtable_duplicate_add duplicates dynamic table
+ * entry at an absolute index |absidx|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_dtable_duplicate_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx);
+
+/*
+ * nghttp3_qpack_encoder_dtable_literal_add adds |nv| to dynamic
+ * table. |token| is a token of name and is -1 if it has no token
+ * value defined. |hash| is a hash of name.
+ *
+ * NGHTTP3_ERR_NOMEM Out of memory.
+ */
+int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder,
+ const nghttp3_nv *nv,
+ int32_t token, uint32_t hash);
+
+/*
+ * `nghttp3_qpack_encoder_ack_header` tells |encoder| that header
+ * block for a stream denoted by |stream_id| was acknowledged by
+ * decoder.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR`
+ * Section Acknowledgement for a stream denoted by |stream_id| is
+ * unexpected.
+ */
+int nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id);
+
+/*
+ * `nghttp3_qpack_encoder_add_icnt` increments known received count of
+ * |encoder| by |n|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR`
+ * |n| is too large.
+ */
+int nghttp3_qpack_encoder_add_icnt(nghttp3_qpack_encoder *encoder, uint64_t n);
+
+/*
+ * `nghttp3_qpack_encoder_cancel_stream` tells |encoder| that stream
+ * denoted by |stream_id| is cancelled. This function is provided for
+ * debugging purpose only. In HTTP/3, |encoder| knows this by reading
+ * decoder stream with `nghttp3_qpack_encoder_read_decoder()`.
+ */
+void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id);
+
+/*
+ * nghttp3_qpack_context_dtable_get returns dynamic table entry whose
+ * absolute index is |absidx|. This function assumes that such entry
+ * exists.
+ */
+nghttp3_qpack_entry *
+nghttp3_qpack_context_dtable_get(nghttp3_qpack_context *ctx, uint64_t absidx);
+
+/*
+ * nghttp3_qpack_context_dtable_top returns latest dynamic table
+ * entry. This function assumes dynamic table is not empty.
+ */
+nghttp3_qpack_entry *
+nghttp3_qpack_context_dtable_top(nghttp3_qpack_context *ctx);
+
+/*
+ * nghttp3_qpack_entry_init initializes |ent|. |qnv| is a header
+ * field. |sum| is the sum of table space occupied by all entries
+ * inserted so far. It does not include this entry. |absidx| is an
+ * absolute index of this entry. |hash| is a hash of header field
+ * name. This function increases reference count of qnv->nv.name and
+ * qnv->nv.value.
+ */
+void nghttp3_qpack_entry_init(nghttp3_qpack_entry *ent, nghttp3_qpack_nv *qnv,
+ size_t sum, uint64_t absidx, uint32_t hash);
+
+/*
+ * nghttp3_qpack_entry_free frees memory allocated for |ent|.
+ */
+void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent);
+
+/*
+ * nghttp3_qpack_put_varint_len returns the required number of bytes
+ * to encode |n| with |prefix| bits.
+ */
+size_t nghttp3_qpack_put_varint_len(uint64_t n, size_t prefix);
+
+/*
+ * nghttp3_qpack_put_varint encodes |n| using variable integer
+ * encoding with |prefix| bits into |buf|. This function assumes the
+ * buffer pointed by |buf| has enough space. This function returns
+ * the one byte beyond the last write (buf +
+ * nghttp3_qpack_put_varint_len(n, prefix)).
+ */
+uint8_t *nghttp3_qpack_put_varint(uint8_t *buf, uint64_t n, size_t prefix);
+
+/* nghttp3_qpack_encoder_stream_state is a set of states for encoder
+ stream decoding. */
+typedef enum nghttp3_qpack_encoder_stream_state {
+ NGHTTP3_QPACK_ES_STATE_OPCODE,
+ NGHTTP3_QPACK_ES_STATE_READ_INDEX,
+ NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN,
+ NGHTTP3_QPACK_ES_STATE_READ_NAMELEN,
+ NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN,
+ NGHTTP3_QPACK_ES_STATE_READ_NAME,
+ NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN,
+ NGHTTP3_QPACK_ES_STATE_READ_VALUELEN,
+ NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN,
+ NGHTTP3_QPACK_ES_STATE_READ_VALUE,
+} nghttp3_qpack_encoder_stream_state;
+
+/* nghttp3_qpack_encoder_stream_opcode is a set of opcodes used in
+ encoder stream. */
+typedef enum nghttp3_qpack_encoder_stream_opcode {
+ NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED,
+ NGHTTP3_QPACK_ES_OPCODE_INSERT,
+ NGHTTP3_QPACK_ES_OPCODE_DUPLICATE,
+ NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP,
+} nghttp3_qpack_encoder_stream_opcode;
+
+/* nghttp3_qpack_request_stream_state is a set of states for request
+ stream decoding. */
+typedef enum nghttp3_qpack_request_stream_state {
+ NGHTTP3_QPACK_RS_STATE_RICNT,
+ NGHTTP3_QPACK_RS_STATE_DBASE_SIGN,
+ NGHTTP3_QPACK_RS_STATE_DBASE,
+ NGHTTP3_QPACK_RS_STATE_OPCODE,
+ NGHTTP3_QPACK_RS_STATE_READ_INDEX,
+ NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN,
+ NGHTTP3_QPACK_RS_STATE_READ_NAMELEN,
+ NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN,
+ NGHTTP3_QPACK_RS_STATE_READ_NAME,
+ NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN,
+ NGHTTP3_QPACK_RS_STATE_READ_VALUELEN,
+ NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN,
+ NGHTTP3_QPACK_RS_STATE_READ_VALUE,
+ NGHTTP3_QPACK_RS_STATE_BLOCKED,
+} nghttp3_qpack_request_stream_state;
+
+/* nghttp3_qpack_request_stream_opcode is a set of opcodes used in
+ request stream. */
+typedef enum nghttp3_qpack_request_stream_opcode {
+ NGHTTP3_QPACK_RS_OPCODE_INDEXED,
+ NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB,
+ NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME,
+ NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB,
+ NGHTTP3_QPACK_RS_OPCODE_LITERAL,
+} nghttp3_qpack_request_stream_opcode;
+
+struct nghttp3_qpack_decoder {
+ nghttp3_qpack_context ctx;
+ /* state is a current state of reading encoder stream. */
+ nghttp3_qpack_encoder_stream_state state;
+ /* opcode is an encoder stream opcode being processed. */
+ nghttp3_qpack_encoder_stream_opcode opcode;
+ /* rstate is a set of intermediate state which are used to process
+ encoder stream. */
+ nghttp3_qpack_read_state rstate;
+ /* dbuf is decoder stream. */
+ nghttp3_buf dbuf;
+ /* written_icnt is Insert Count written to decoder stream so far. */
+ uint64_t written_icnt;
+ /* max_concurrent_streams is the number of concurrent streams that a
+ remote endpoint can open, including both bidirectional and
+ unidirectional streams which potentially receives QPACK encoded
+ HEADER frame. */
+ size_t max_concurrent_streams;
+};
+
+/*
+ * nghttp3_qpack_decoder_init initializes |decoder|.
+ * |hard_max_dtable_capacity| is the upper bound of the dynamic table
+ * capacity. |max_blocked_streams| is the maximum number of stream
+ * which can be blocked. |mem| is a memory allocator.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_qpack_decoder_free frees memory allocated for |decoder|.
+ * This function does not free memory pointed by |decoder|.
+ */
+void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_indexed_add adds entry received in
+ * Insert With Name Reference to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_static_add adds entry received in
+ * Insert With Name Reference (static) to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_dynamic_add adds entry received in
+ * Insert With Name Reference (dynamic) to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_duplicate_add adds entry received in
+ * Duplicate to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_literal_add adds entry received in
+ * Insert With Literal Name to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder);
+
+struct nghttp3_qpack_stream_context {
+ /* state is a current state of reading request stream. */
+ nghttp3_qpack_request_stream_state state;
+ /* rstate is a set of intermediate state which are used to process
+ request stream. */
+ nghttp3_qpack_read_state rstate;
+ const nghttp3_mem *mem;
+ /* opcode is a request stream opcode being processed. */
+ nghttp3_qpack_request_stream_opcode opcode;
+ int64_t stream_id;
+ /* ricnt is Required Insert Count to decode this header block. */
+ uint64_t ricnt;
+ /* base is Base in Header Block Prefix. */
+ uint64_t base;
+ /* dbase_sign is the delta base sign in Header Block Prefix. */
+ int dbase_sign;
+};
+
+/*
+ * nghttp3_qpack_stream_context_init initializes |sctx|.
+ */
+void nghttp3_qpack_stream_context_init(nghttp3_qpack_stream_context *sctx,
+ int64_t stream_id,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_qpack_stream_context_free frees memory allocated for
+ * |sctx|. This function does not free memory pointed by |sctx|.
+ */
+void nghttp3_qpack_stream_context_free(nghttp3_qpack_stream_context *sctx);
+
+/*
+ * nghttp3_qpack_decoder_reconstruct_ricnt reconstructs Required
+ * Insert Count from the encoded form |encricnt| and stores Required
+ * Insert Count in |*dest|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED
+ * Unable to reconstruct Required Insert Count.
+ */
+int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder,
+ uint64_t *dest, uint64_t encricnt);
+
+/*
+ * nghttp3_qpack_decoder_rel2abs converts relative index rstate->left
+ * received in encoder stream to absolute index and stores it in
+ * rstate->absidx.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Relative index is invalid.
+ */
+int nghttp3_qpack_decoder_rel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_read_state *rstate);
+
+/*
+ * nghttp3_qpack_decoder_brel2abs converts Base relative index
+ * rstate->left received in request stream to absolute index and
+ * stores it in rstate->absidx.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED
+ * Base relative index is invalid.
+ */
+int nghttp3_qpack_decoder_brel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx);
+
+/*
+ * nghttp3_qpack_decoder_pbrel2abs converts Post-Base relative index
+ * rstate->left received in request stream to absolute index and
+ * stores it in rstate->absidx.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED
+ * Post-Base relative index is invalid.
+ */
+int nghttp3_qpack_decoder_pbrel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx);
+
+void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv);
+
+int nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv);
+
+void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv);
+
+/*
+ * nghttp3_qpack_decoder_write_section_ack writes Section
+ * Acknowledgement to decoder stream.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_FATAL
+ * Decoder stream overflow.
+ */
+int nghttp3_qpack_decoder_write_section_ack(
+ nghttp3_qpack_decoder *decoder, const nghttp3_qpack_stream_context *sctx);
+
+#endif /* NGHTTP3_QPACK_H */
diff --git a/lib/nghttp3_qpack_huffman.c b/lib/nghttp3_qpack_huffman.c
new file mode 100644
index 0000000..c36a68e
--- /dev/null
+++ b/lib/nghttp3_qpack_huffman.c
@@ -0,0 +1,122 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_qpack_huffman.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "nghttp3_conv.h"
+
+size_t nghttp3_qpack_huffman_encode_count(const uint8_t *src, size_t len) {
+ size_t i;
+ size_t nbits = 0;
+
+ for (i = 0; i < len; ++i) {
+ nbits += huffman_sym_table[src[i]].nbits;
+ }
+ /* pad the prefix of EOS (256) */
+ return (nbits + 7) / 8;
+}
+
+uint8_t *nghttp3_qpack_huffman_encode(uint8_t *dest, const uint8_t *src,
+ size_t srclen) {
+ const nghttp3_qpack_huffman_sym *sym;
+ const uint8_t *end = src + srclen;
+ uint64_t code = 0;
+ size_t nbits = 0;
+ uint32_t x;
+
+ for (; src != end;) {
+ sym = &huffman_sym_table[*src++];
+ code |= (uint64_t)sym->code << (32 - nbits);
+ nbits += sym->nbits;
+ if (nbits < 32) {
+ continue;
+ }
+ x = htonl((uint32_t)(code >> 32));
+ memcpy(dest, &x, 4);
+ dest += 4;
+ code <<= 32;
+ nbits -= 32;
+ }
+
+ for (; nbits >= 8;) {
+ *dest++ = (uint8_t)(code >> 56);
+ code <<= 8;
+ nbits -= 8;
+ }
+
+ if (nbits) {
+ *dest++ = (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1));
+ }
+
+ return dest;
+}
+
+void nghttp3_qpack_huffman_decode_context_init(
+ nghttp3_qpack_huffman_decode_context *ctx) {
+ ctx->fstate = NGHTTP3_QPACK_HUFFMAN_ACCEPTED;
+}
+
+nghttp3_ssize
+nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx,
+ uint8_t *dest, const uint8_t *src, size_t srclen,
+ int fin) {
+ uint8_t *p = dest;
+ const uint8_t *end = src + srclen;
+ nghttp3_qpack_huffman_decode_node node = {ctx->fstate, 0};
+ const nghttp3_qpack_huffman_decode_node *t = &node;
+ uint8_t c;
+
+ /* We use the decoding algorithm described in
+ http://graphics.ics.uci.edu/pub/Prefix.pdf */
+ for (; src != end;) {
+ c = *src++;
+ t = &qpack_huffman_decode_table[t->fstate & 0x1ff][c >> 4];
+ if (t->fstate & NGHTTP3_QPACK_HUFFMAN_SYM) {
+ *p++ = t->sym;
+ }
+
+ t = &qpack_huffman_decode_table[t->fstate & 0x1ff][c & 0xf];
+ if (t->fstate & NGHTTP3_QPACK_HUFFMAN_SYM) {
+ *p++ = t->sym;
+ }
+ }
+
+ ctx->fstate = t->fstate;
+
+ if (fin && !(ctx->fstate & NGHTTP3_QPACK_HUFFMAN_ACCEPTED)) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ return p - dest;
+}
+
+int nghttp3_qpack_huffman_decode_failure_state(
+ nghttp3_qpack_huffman_decode_context *ctx) {
+ return ctx->fstate == 0x100;
+}
diff --git a/lib/nghttp3_qpack_huffman.h b/lib/nghttp3_qpack_huffman.h
new file mode 100644
index 0000000..fc3bc7b
--- /dev/null
+++ b/lib/nghttp3_qpack_huffman.h
@@ -0,0 +1,108 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_QPACK_HUFFMAN_H
+#define NGHTTP3_QPACK_HUFFMAN_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+typedef struct nghttp3_qpack_huffman_sym {
+ /* The number of bits in this code */
+ uint32_t nbits;
+ /* Huffman code aligned to LSB */
+ uint32_t code;
+} nghttp3_qpack_huffman_sym;
+
+extern const nghttp3_qpack_huffman_sym huffman_sym_table[];
+
+size_t nghttp3_qpack_huffman_encode_count(const uint8_t *src, size_t len);
+
+uint8_t *nghttp3_qpack_huffman_encode(uint8_t *dest, const uint8_t *src,
+ size_t srclen);
+
+typedef enum nghttp3_qpack_huffman_decode_flag {
+ /* FSA accepts this state as the end of huffman encoding
+ sequence. */
+ NGHTTP3_QPACK_HUFFMAN_ACCEPTED = 1 << 14,
+ /* This state emits symbol */
+ NGHTTP3_QPACK_HUFFMAN_SYM = 1 << 15,
+} nghttp3_qpack_huffman_decode_flag;
+
+typedef struct nghttp3_qpack_huffman_decode_node {
+ /* fstate is the current huffman decoding state, which is actually
+ the node ID of internal huffman tree with
+ nghttp3_qpack_huffman_decode_flag OR-ed. We have 257 leaf nodes,
+ but they are identical to root node other than emitting a symbol,
+ so we have 256 internal nodes [1..256], inclusive. The node ID
+ 256 is a special node and it is a terminal state that means
+ decoding failed. */
+ uint16_t fstate;
+ /* symbol if NGHTTP3_QPACK_HUFFMAN_SYM flag set */
+ uint8_t sym;
+} nghttp3_qpack_huffman_decode_node;
+
+typedef struct nghttp3_qpack_huffman_decode_context {
+ /* fstate is the current huffman decoding state. */
+ uint16_t fstate;
+} nghttp3_qpack_huffman_decode_context;
+
+extern const nghttp3_qpack_huffman_decode_node qpack_huffman_decode_table[][16];
+
+void nghttp3_qpack_huffman_decode_context_init(
+ nghttp3_qpack_huffman_decode_context *ctx);
+
+/*
+ * nghttp3_qpack_huffman_decode decodes huffman encoded byte string
+ * stored in |src| of length |srclen|. |ctx| is a decoding context.
+ * |ctx| remembers the decoding state, and caller can call this
+ * function multiple times to feed each chunk of huffman encoded
+ * substring. |fin| must be nonzero if |src| contains the last chunk
+ * of huffman string. The decoded string is written to the buffer
+ * pointed by |dest|. This function assumes that the buffer pointed
+ * by |dest| contains enough memory to store decoded byte string.
+ *
+ * This function returns the number of bytes written to |dest|, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_FATAL
+ * Could not decode huffman string.
+ */
+nghttp3_ssize
+nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx,
+ uint8_t *dest, const uint8_t *src, size_t srclen,
+ int fin);
+
+/*
+ * nghttp3_qpack_huffman_decode_failure_state returns nonzero if |ctx|
+ * indicates that huffman decoding context is in failure state.
+ */
+int nghttp3_qpack_huffman_decode_failure_state(
+ nghttp3_qpack_huffman_decode_context *ctx);
+
+#endif /* NGHTTP3_QPACK_HUFFMAN_H */
diff --git a/lib/nghttp3_qpack_huffman_data.c b/lib/nghttp3_qpack_huffman_data.c
new file mode 100644
index 0000000..0c104db
--- /dev/null
+++ b/lib/nghttp3_qpack_huffman_data.c
@@ -0,0 +1,4981 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_qpack_huffman.h"
+
+/* Generated by mkhufftbl.py */
+
+const nghttp3_qpack_huffman_sym huffman_sym_table[] = {
+ {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u},
+ {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u},
+ {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u},
+ {28, 0xfffffea0u}, {30, 0xfffffff4u}, {28, 0xfffffeb0u}, {28, 0xfffffec0u},
+ {28, 0xfffffed0u}, {28, 0xfffffee0u}, {28, 0xfffffef0u}, {28, 0xffffff00u},
+ {28, 0xffffff10u}, {28, 0xffffff20u}, {30, 0xfffffff8u}, {28, 0xffffff30u},
+ {28, 0xffffff40u}, {28, 0xffffff50u}, {28, 0xffffff60u}, {28, 0xffffff70u},
+ {28, 0xffffff80u}, {28, 0xffffff90u}, {28, 0xffffffa0u}, {28, 0xffffffb0u},
+ {6, 0x50000000u}, {10, 0xfe000000u}, {10, 0xfe400000u}, {12, 0xffa00000u},
+ {13, 0xffc80000u}, {6, 0x54000000u}, {8, 0xf8000000u}, {11, 0xff400000u},
+ {10, 0xfe800000u}, {10, 0xfec00000u}, {8, 0xf9000000u}, {11, 0xff600000u},
+ {8, 0xfa000000u}, {6, 0x58000000u}, {6, 0x5c000000u}, {6, 0x60000000u},
+ {5, 0x0u}, {5, 0x8000000u}, {5, 0x10000000u}, {6, 0x64000000u},
+ {6, 0x68000000u}, {6, 0x6c000000u}, {6, 0x70000000u}, {6, 0x74000000u},
+ {6, 0x78000000u}, {6, 0x7c000000u}, {7, 0xb8000000u}, {8, 0xfb000000u},
+ {15, 0xfff80000u}, {6, 0x80000000u}, {12, 0xffb00000u}, {10, 0xff000000u},
+ {13, 0xffd00000u}, {6, 0x84000000u}, {7, 0xba000000u}, {7, 0xbc000000u},
+ {7, 0xbe000000u}, {7, 0xc0000000u}, {7, 0xc2000000u}, {7, 0xc4000000u},
+ {7, 0xc6000000u}, {7, 0xc8000000u}, {7, 0xca000000u}, {7, 0xcc000000u},
+ {7, 0xce000000u}, {7, 0xd0000000u}, {7, 0xd2000000u}, {7, 0xd4000000u},
+ {7, 0xd6000000u}, {7, 0xd8000000u}, {7, 0xda000000u}, {7, 0xdc000000u},
+ {7, 0xde000000u}, {7, 0xe0000000u}, {7, 0xe2000000u}, {7, 0xe4000000u},
+ {8, 0xfc000000u}, {7, 0xe6000000u}, {8, 0xfd000000u}, {13, 0xffd80000u},
+ {19, 0xfffe0000u}, {13, 0xffe00000u}, {14, 0xfff00000u}, {6, 0x88000000u},
+ {15, 0xfffa0000u}, {5, 0x18000000u}, {6, 0x8c000000u}, {5, 0x20000000u},
+ {6, 0x90000000u}, {5, 0x28000000u}, {6, 0x94000000u}, {6, 0x98000000u},
+ {6, 0x9c000000u}, {5, 0x30000000u}, {7, 0xe8000000u}, {7, 0xea000000u},
+ {6, 0xa0000000u}, {6, 0xa4000000u}, {6, 0xa8000000u}, {5, 0x38000000u},
+ {6, 0xac000000u}, {7, 0xec000000u}, {6, 0xb0000000u}, {5, 0x40000000u},
+ {5, 0x48000000u}, {6, 0xb4000000u}, {7, 0xee000000u}, {7, 0xf0000000u},
+ {7, 0xf2000000u}, {7, 0xf4000000u}, {7, 0xf6000000u}, {15, 0xfffc0000u},
+ {11, 0xff800000u}, {14, 0xfff40000u}, {13, 0xffe80000u}, {28, 0xffffffc0u},
+ {20, 0xfffe6000u}, {22, 0xffff4800u}, {20, 0xfffe7000u}, {20, 0xfffe8000u},
+ {22, 0xffff4c00u}, {22, 0xffff5000u}, {22, 0xffff5400u}, {23, 0xffffb200u},
+ {22, 0xffff5800u}, {23, 0xffffb400u}, {23, 0xffffb600u}, {23, 0xffffb800u},
+ {23, 0xffffba00u}, {23, 0xffffbc00u}, {24, 0xffffeb00u}, {23, 0xffffbe00u},
+ {24, 0xffffec00u}, {24, 0xffffed00u}, {22, 0xffff5c00u}, {23, 0xffffc000u},
+ {24, 0xffffee00u}, {23, 0xffffc200u}, {23, 0xffffc400u}, {23, 0xffffc600u},
+ {23, 0xffffc800u}, {21, 0xfffee000u}, {22, 0xffff6000u}, {23, 0xffffca00u},
+ {22, 0xffff6400u}, {23, 0xffffcc00u}, {23, 0xffffce00u}, {24, 0xffffef00u},
+ {22, 0xffff6800u}, {21, 0xfffee800u}, {20, 0xfffe9000u}, {22, 0xffff6c00u},
+ {22, 0xffff7000u}, {23, 0xffffd000u}, {23, 0xffffd200u}, {21, 0xfffef000u},
+ {23, 0xffffd400u}, {22, 0xffff7400u}, {22, 0xffff7800u}, {24, 0xfffff000u},
+ {21, 0xfffef800u}, {22, 0xffff7c00u}, {23, 0xffffd600u}, {23, 0xffffd800u},
+ {21, 0xffff0000u}, {21, 0xffff0800u}, {22, 0xffff8000u}, {21, 0xffff1000u},
+ {23, 0xffffda00u}, {22, 0xffff8400u}, {23, 0xffffdc00u}, {23, 0xffffde00u},
+ {20, 0xfffea000u}, {22, 0xffff8800u}, {22, 0xffff8c00u}, {22, 0xffff9000u},
+ {23, 0xffffe000u}, {22, 0xffff9400u}, {22, 0xffff9800u}, {23, 0xffffe200u},
+ {26, 0xfffff800u}, {26, 0xfffff840u}, {20, 0xfffeb000u}, {19, 0xfffe2000u},
+ {22, 0xffff9c00u}, {23, 0xffffe400u}, {22, 0xffffa000u}, {25, 0xfffff600u},
+ {26, 0xfffff880u}, {26, 0xfffff8c0u}, {26, 0xfffff900u}, {27, 0xfffffbc0u},
+ {27, 0xfffffbe0u}, {26, 0xfffff940u}, {24, 0xfffff100u}, {25, 0xfffff680u},
+ {19, 0xfffe4000u}, {21, 0xffff1800u}, {26, 0xfffff980u}, {27, 0xfffffc00u},
+ {27, 0xfffffc20u}, {26, 0xfffff9c0u}, {27, 0xfffffc40u}, {24, 0xfffff200u},
+ {21, 0xffff2000u}, {21, 0xffff2800u}, {26, 0xfffffa00u}, {26, 0xfffffa40u},
+ {28, 0xffffffd0u}, {27, 0xfffffc60u}, {27, 0xfffffc80u}, {27, 0xfffffca0u},
+ {20, 0xfffec000u}, {24, 0xfffff300u}, {20, 0xfffed000u}, {21, 0xffff3000u},
+ {22, 0xffffa400u}, {21, 0xffff3800u}, {21, 0xffff4000u}, {23, 0xffffe600u},
+ {22, 0xffffa800u}, {22, 0xffffac00u}, {25, 0xfffff700u}, {25, 0xfffff780u},
+ {24, 0xfffff400u}, {24, 0xfffff500u}, {26, 0xfffffa80u}, {23, 0xffffe800u},
+ {26, 0xfffffac0u}, {27, 0xfffffcc0u}, {26, 0xfffffb00u}, {26, 0xfffffb40u},
+ {27, 0xfffffce0u}, {27, 0xfffffd00u}, {27, 0xfffffd20u}, {27, 0xfffffd40u},
+ {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u},
+ {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u},
+ {30, 0xfffffffcu}};
+
+const nghttp3_qpack_huffman_decode_node qpack_huffman_decode_table[][16] = {
+ /* 0 */
+ {
+ {0x04, 0},
+ {0x05, 0},
+ {0x07, 0},
+ {0x08, 0},
+ {0x0b, 0},
+ {0x0c, 0},
+ {0x10, 0},
+ {0x13, 0},
+ {0x19, 0},
+ {0x1c, 0},
+ {0x20, 0},
+ {0x23, 0},
+ {0x2a, 0},
+ {0x31, 0},
+ {0x39, 0},
+ {0x4040, 0},
+ },
+ /* 1 */
+ {
+ {0xc000, 48},
+ {0xc000, 49},
+ {0xc000, 50},
+ {0xc000, 97},
+ {0xc000, 99},
+ {0xc000, 101},
+ {0xc000, 105},
+ {0xc000, 111},
+ {0xc000, 115},
+ {0xc000, 116},
+ {0x0d, 0},
+ {0x0e, 0},
+ {0x11, 0},
+ {0x12, 0},
+ {0x14, 0},
+ {0x15, 0},
+ },
+ /* 2 */
+ {
+ {0x8001, 48},
+ {0xc016, 48},
+ {0x8001, 49},
+ {0xc016, 49},
+ {0x8001, 50},
+ {0xc016, 50},
+ {0x8001, 97},
+ {0xc016, 97},
+ {0x8001, 99},
+ {0xc016, 99},
+ {0x8001, 101},
+ {0xc016, 101},
+ {0x8001, 105},
+ {0xc016, 105},
+ {0x8001, 111},
+ {0xc016, 111},
+ },
+ /* 3 */
+ {
+ {0x8002, 48},
+ {0x8009, 48},
+ {0x8017, 48},
+ {0xc028, 48},
+ {0x8002, 49},
+ {0x8009, 49},
+ {0x8017, 49},
+ {0xc028, 49},
+ {0x8002, 50},
+ {0x8009, 50},
+ {0x8017, 50},
+ {0xc028, 50},
+ {0x8002, 97},
+ {0x8009, 97},
+ {0x8017, 97},
+ {0xc028, 97},
+ },
+ /* 4 */
+ {
+ {0x8003, 48},
+ {0x8006, 48},
+ {0x800a, 48},
+ {0x800f, 48},
+ {0x8018, 48},
+ {0x801f, 48},
+ {0x8029, 48},
+ {0xc038, 48},
+ {0x8003, 49},
+ {0x8006, 49},
+ {0x800a, 49},
+ {0x800f, 49},
+ {0x8018, 49},
+ {0x801f, 49},
+ {0x8029, 49},
+ {0xc038, 49},
+ },
+ /* 5 */
+ {
+ {0x8003, 50},
+ {0x8006, 50},
+ {0x800a, 50},
+ {0x800f, 50},
+ {0x8018, 50},
+ {0x801f, 50},
+ {0x8029, 50},
+ {0xc038, 50},
+ {0x8003, 97},
+ {0x8006, 97},
+ {0x800a, 97},
+ {0x800f, 97},
+ {0x8018, 97},
+ {0x801f, 97},
+ {0x8029, 97},
+ {0xc038, 97},
+ },
+ /* 6 */
+ {
+ {0x8002, 99},
+ {0x8009, 99},
+ {0x8017, 99},
+ {0xc028, 99},
+ {0x8002, 101},
+ {0x8009, 101},
+ {0x8017, 101},
+ {0xc028, 101},
+ {0x8002, 105},
+ {0x8009, 105},
+ {0x8017, 105},
+ {0xc028, 105},
+ {0x8002, 111},
+ {0x8009, 111},
+ {0x8017, 111},
+ {0xc028, 111},
+ },
+ /* 7 */
+ {
+ {0x8003, 99},
+ {0x8006, 99},
+ {0x800a, 99},
+ {0x800f, 99},
+ {0x8018, 99},
+ {0x801f, 99},
+ {0x8029, 99},
+ {0xc038, 99},
+ {0x8003, 101},
+ {0x8006, 101},
+ {0x800a, 101},
+ {0x800f, 101},
+ {0x8018, 101},
+ {0x801f, 101},
+ {0x8029, 101},
+ {0xc038, 101},
+ },
+ /* 8 */
+ {
+ {0x8003, 105},
+ {0x8006, 105},
+ {0x800a, 105},
+ {0x800f, 105},
+ {0x8018, 105},
+ {0x801f, 105},
+ {0x8029, 105},
+ {0xc038, 105},
+ {0x8003, 111},
+ {0x8006, 111},
+ {0x800a, 111},
+ {0x800f, 111},
+ {0x8018, 111},
+ {0x801f, 111},
+ {0x8029, 111},
+ {0xc038, 111},
+ },
+ /* 9 */
+ {
+ {0x8001, 115},
+ {0xc016, 115},
+ {0x8001, 116},
+ {0xc016, 116},
+ {0xc000, 32},
+ {0xc000, 37},
+ {0xc000, 45},
+ {0xc000, 46},
+ {0xc000, 47},
+ {0xc000, 51},
+ {0xc000, 52},
+ {0xc000, 53},
+ {0xc000, 54},
+ {0xc000, 55},
+ {0xc000, 56},
+ {0xc000, 57},
+ },
+ /* 10 */
+ {
+ {0x8002, 115},
+ {0x8009, 115},
+ {0x8017, 115},
+ {0xc028, 115},
+ {0x8002, 116},
+ {0x8009, 116},
+ {0x8017, 116},
+ {0xc028, 116},
+ {0x8001, 32},
+ {0xc016, 32},
+ {0x8001, 37},
+ {0xc016, 37},
+ {0x8001, 45},
+ {0xc016, 45},
+ {0x8001, 46},
+ {0xc016, 46},
+ },
+ /* 11 */
+ {
+ {0x8003, 115},
+ {0x8006, 115},
+ {0x800a, 115},
+ {0x800f, 115},
+ {0x8018, 115},
+ {0x801f, 115},
+ {0x8029, 115},
+ {0xc038, 115},
+ {0x8003, 116},
+ {0x8006, 116},
+ {0x800a, 116},
+ {0x800f, 116},
+ {0x8018, 116},
+ {0x801f, 116},
+ {0x8029, 116},
+ {0xc038, 116},
+ },
+ /* 12 */
+ {
+ {0x8002, 32},
+ {0x8009, 32},
+ {0x8017, 32},
+ {0xc028, 32},
+ {0x8002, 37},
+ {0x8009, 37},
+ {0x8017, 37},
+ {0xc028, 37},
+ {0x8002, 45},
+ {0x8009, 45},
+ {0x8017, 45},
+ {0xc028, 45},
+ {0x8002, 46},
+ {0x8009, 46},
+ {0x8017, 46},
+ {0xc028, 46},
+ },
+ /* 13 */
+ {
+ {0x8003, 32},
+ {0x8006, 32},
+ {0x800a, 32},
+ {0x800f, 32},
+ {0x8018, 32},
+ {0x801f, 32},
+ {0x8029, 32},
+ {0xc038, 32},
+ {0x8003, 37},
+ {0x8006, 37},
+ {0x800a, 37},
+ {0x800f, 37},
+ {0x8018, 37},
+ {0x801f, 37},
+ {0x8029, 37},
+ {0xc038, 37},
+ },
+ /* 14 */
+ {
+ {0x8003, 45},
+ {0x8006, 45},
+ {0x800a, 45},
+ {0x800f, 45},
+ {0x8018, 45},
+ {0x801f, 45},
+ {0x8029, 45},
+ {0xc038, 45},
+ {0x8003, 46},
+ {0x8006, 46},
+ {0x800a, 46},
+ {0x800f, 46},
+ {0x8018, 46},
+ {0x801f, 46},
+ {0x8029, 46},
+ {0xc038, 46},
+ },
+ /* 15 */
+ {
+ {0x8001, 47},
+ {0xc016, 47},
+ {0x8001, 51},
+ {0xc016, 51},
+ {0x8001, 52},
+ {0xc016, 52},
+ {0x8001, 53},
+ {0xc016, 53},
+ {0x8001, 54},
+ {0xc016, 54},
+ {0x8001, 55},
+ {0xc016, 55},
+ {0x8001, 56},
+ {0xc016, 56},
+ {0x8001, 57},
+ {0xc016, 57},
+ },
+ /* 16 */
+ {
+ {0x8002, 47},
+ {0x8009, 47},
+ {0x8017, 47},
+ {0xc028, 47},
+ {0x8002, 51},
+ {0x8009, 51},
+ {0x8017, 51},
+ {0xc028, 51},
+ {0x8002, 52},
+ {0x8009, 52},
+ {0x8017, 52},
+ {0xc028, 52},
+ {0x8002, 53},
+ {0x8009, 53},
+ {0x8017, 53},
+ {0xc028, 53},
+ },
+ /* 17 */
+ {
+ {0x8003, 47},
+ {0x8006, 47},
+ {0x800a, 47},
+ {0x800f, 47},
+ {0x8018, 47},
+ {0x801f, 47},
+ {0x8029, 47},
+ {0xc038, 47},
+ {0x8003, 51},
+ {0x8006, 51},
+ {0x800a, 51},
+ {0x800f, 51},
+ {0x8018, 51},
+ {0x801f, 51},
+ {0x8029, 51},
+ {0xc038, 51},
+ },
+ /* 18 */
+ {
+ {0x8003, 52},
+ {0x8006, 52},
+ {0x800a, 52},
+ {0x800f, 52},
+ {0x8018, 52},
+ {0x801f, 52},
+ {0x8029, 52},
+ {0xc038, 52},
+ {0x8003, 53},
+ {0x8006, 53},
+ {0x800a, 53},
+ {0x800f, 53},
+ {0x8018, 53},
+ {0x801f, 53},
+ {0x8029, 53},
+ {0xc038, 53},
+ },
+ /* 19 */
+ {
+ {0x8002, 54},
+ {0x8009, 54},
+ {0x8017, 54},
+ {0xc028, 54},
+ {0x8002, 55},
+ {0x8009, 55},
+ {0x8017, 55},
+ {0xc028, 55},
+ {0x8002, 56},
+ {0x8009, 56},
+ {0x8017, 56},
+ {0xc028, 56},
+ {0x8002, 57},
+ {0x8009, 57},
+ {0x8017, 57},
+ {0xc028, 57},
+ },
+ /* 20 */
+ {
+ {0x8003, 54},
+ {0x8006, 54},
+ {0x800a, 54},
+ {0x800f, 54},
+ {0x8018, 54},
+ {0x801f, 54},
+ {0x8029, 54},
+ {0xc038, 54},
+ {0x8003, 55},
+ {0x8006, 55},
+ {0x800a, 55},
+ {0x800f, 55},
+ {0x8018, 55},
+ {0x801f, 55},
+ {0x8029, 55},
+ {0xc038, 55},
+ },
+ /* 21 */
+ {
+ {0x8003, 56},
+ {0x8006, 56},
+ {0x800a, 56},
+ {0x800f, 56},
+ {0x8018, 56},
+ {0x801f, 56},
+ {0x8029, 56},
+ {0xc038, 56},
+ {0x8003, 57},
+ {0x8006, 57},
+ {0x800a, 57},
+ {0x800f, 57},
+ {0x8018, 57},
+ {0x801f, 57},
+ {0x8029, 57},
+ {0xc038, 57},
+ },
+ /* 22 */
+ {
+ {0x1a, 0},
+ {0x1b, 0},
+ {0x1d, 0},
+ {0x1e, 0},
+ {0x21, 0},
+ {0x22, 0},
+ {0x24, 0},
+ {0x25, 0},
+ {0x2b, 0},
+ {0x2e, 0},
+ {0x32, 0},
+ {0x35, 0},
+ {0x3a, 0},
+ {0x3d, 0},
+ {0x41, 0},
+ {0x4044, 0},
+ },
+ /* 23 */
+ {
+ {0xc000, 61},
+ {0xc000, 65},
+ {0xc000, 95},
+ {0xc000, 98},
+ {0xc000, 100},
+ {0xc000, 102},
+ {0xc000, 103},
+ {0xc000, 104},
+ {0xc000, 108},
+ {0xc000, 109},
+ {0xc000, 110},
+ {0xc000, 112},
+ {0xc000, 114},
+ {0xc000, 117},
+ {0x26, 0},
+ {0x27, 0},
+ },
+ /* 24 */
+ {
+ {0x8001, 61},
+ {0xc016, 61},
+ {0x8001, 65},
+ {0xc016, 65},
+ {0x8001, 95},
+ {0xc016, 95},
+ {0x8001, 98},
+ {0xc016, 98},
+ {0x8001, 100},
+ {0xc016, 100},
+ {0x8001, 102},
+ {0xc016, 102},
+ {0x8001, 103},
+ {0xc016, 103},
+ {0x8001, 104},
+ {0xc016, 104},
+ },
+ /* 25 */
+ {
+ {0x8002, 61},
+ {0x8009, 61},
+ {0x8017, 61},
+ {0xc028, 61},
+ {0x8002, 65},
+ {0x8009, 65},
+ {0x8017, 65},
+ {0xc028, 65},
+ {0x8002, 95},
+ {0x8009, 95},
+ {0x8017, 95},
+ {0xc028, 95},
+ {0x8002, 98},
+ {0x8009, 98},
+ {0x8017, 98},
+ {0xc028, 98},
+ },
+ /* 26 */
+ {
+ {0x8003, 61},
+ {0x8006, 61},
+ {0x800a, 61},
+ {0x800f, 61},
+ {0x8018, 61},
+ {0x801f, 61},
+ {0x8029, 61},
+ {0xc038, 61},
+ {0x8003, 65},
+ {0x8006, 65},
+ {0x800a, 65},
+ {0x800f, 65},
+ {0x8018, 65},
+ {0x801f, 65},
+ {0x8029, 65},
+ {0xc038, 65},
+ },
+ /* 27 */
+ {
+ {0x8003, 95},
+ {0x8006, 95},
+ {0x800a, 95},
+ {0x800f, 95},
+ {0x8018, 95},
+ {0x801f, 95},
+ {0x8029, 95},
+ {0xc038, 95},
+ {0x8003, 98},
+ {0x8006, 98},
+ {0x800a, 98},
+ {0x800f, 98},
+ {0x8018, 98},
+ {0x801f, 98},
+ {0x8029, 98},
+ {0xc038, 98},
+ },
+ /* 28 */
+ {
+ {0x8002, 100},
+ {0x8009, 100},
+ {0x8017, 100},
+ {0xc028, 100},
+ {0x8002, 102},
+ {0x8009, 102},
+ {0x8017, 102},
+ {0xc028, 102},
+ {0x8002, 103},
+ {0x8009, 103},
+ {0x8017, 103},
+ {0xc028, 103},
+ {0x8002, 104},
+ {0x8009, 104},
+ {0x8017, 104},
+ {0xc028, 104},
+ },
+ /* 29 */
+ {
+ {0x8003, 100},
+ {0x8006, 100},
+ {0x800a, 100},
+ {0x800f, 100},
+ {0x8018, 100},
+ {0x801f, 100},
+ {0x8029, 100},
+ {0xc038, 100},
+ {0x8003, 102},
+ {0x8006, 102},
+ {0x800a, 102},
+ {0x800f, 102},
+ {0x8018, 102},
+ {0x801f, 102},
+ {0x8029, 102},
+ {0xc038, 102},
+ },
+ /* 30 */
+ {
+ {0x8003, 103},
+ {0x8006, 103},
+ {0x800a, 103},
+ {0x800f, 103},
+ {0x8018, 103},
+ {0x801f, 103},
+ {0x8029, 103},
+ {0xc038, 103},
+ {0x8003, 104},
+ {0x8006, 104},
+ {0x800a, 104},
+ {0x800f, 104},
+ {0x8018, 104},
+ {0x801f, 104},
+ {0x8029, 104},
+ {0xc038, 104},
+ },
+ /* 31 */
+ {
+ {0x8001, 108},
+ {0xc016, 108},
+ {0x8001, 109},
+ {0xc016, 109},
+ {0x8001, 110},
+ {0xc016, 110},
+ {0x8001, 112},
+ {0xc016, 112},
+ {0x8001, 114},
+ {0xc016, 114},
+ {0x8001, 117},
+ {0xc016, 117},
+ {0xc000, 58},
+ {0xc000, 66},
+ {0xc000, 67},
+ {0xc000, 68},
+ },
+ /* 32 */
+ {
+ {0x8002, 108},
+ {0x8009, 108},
+ {0x8017, 108},
+ {0xc028, 108},
+ {0x8002, 109},
+ {0x8009, 109},
+ {0x8017, 109},
+ {0xc028, 109},
+ {0x8002, 110},
+ {0x8009, 110},
+ {0x8017, 110},
+ {0xc028, 110},
+ {0x8002, 112},
+ {0x8009, 112},
+ {0x8017, 112},
+ {0xc028, 112},
+ },
+ /* 33 */
+ {
+ {0x8003, 108},
+ {0x8006, 108},
+ {0x800a, 108},
+ {0x800f, 108},
+ {0x8018, 108},
+ {0x801f, 108},
+ {0x8029, 108},
+ {0xc038, 108},
+ {0x8003, 109},
+ {0x8006, 109},
+ {0x800a, 109},
+ {0x800f, 109},
+ {0x8018, 109},
+ {0x801f, 109},
+ {0x8029, 109},
+ {0xc038, 109},
+ },
+ /* 34 */
+ {
+ {0x8003, 110},
+ {0x8006, 110},
+ {0x800a, 110},
+ {0x800f, 110},
+ {0x8018, 110},
+ {0x801f, 110},
+ {0x8029, 110},
+ {0xc038, 110},
+ {0x8003, 112},
+ {0x8006, 112},
+ {0x800a, 112},
+ {0x800f, 112},
+ {0x8018, 112},
+ {0x801f, 112},
+ {0x8029, 112},
+ {0xc038, 112},
+ },
+ /* 35 */
+ {
+ {0x8002, 114},
+ {0x8009, 114},
+ {0x8017, 114},
+ {0xc028, 114},
+ {0x8002, 117},
+ {0x8009, 117},
+ {0x8017, 117},
+ {0xc028, 117},
+ {0x8001, 58},
+ {0xc016, 58},
+ {0x8001, 66},
+ {0xc016, 66},
+ {0x8001, 67},
+ {0xc016, 67},
+ {0x8001, 68},
+ {0xc016, 68},
+ },
+ /* 36 */
+ {
+ {0x8003, 114},
+ {0x8006, 114},
+ {0x800a, 114},
+ {0x800f, 114},
+ {0x8018, 114},
+ {0x801f, 114},
+ {0x8029, 114},
+ {0xc038, 114},
+ {0x8003, 117},
+ {0x8006, 117},
+ {0x800a, 117},
+ {0x800f, 117},
+ {0x8018, 117},
+ {0x801f, 117},
+ {0x8029, 117},
+ {0xc038, 117},
+ },
+ /* 37 */
+ {
+ {0x8002, 58},
+ {0x8009, 58},
+ {0x8017, 58},
+ {0xc028, 58},
+ {0x8002, 66},
+ {0x8009, 66},
+ {0x8017, 66},
+ {0xc028, 66},
+ {0x8002, 67},
+ {0x8009, 67},
+ {0x8017, 67},
+ {0xc028, 67},
+ {0x8002, 68},
+ {0x8009, 68},
+ {0x8017, 68},
+ {0xc028, 68},
+ },
+ /* 38 */
+ {
+ {0x8003, 58},
+ {0x8006, 58},
+ {0x800a, 58},
+ {0x800f, 58},
+ {0x8018, 58},
+ {0x801f, 58},
+ {0x8029, 58},
+ {0xc038, 58},
+ {0x8003, 66},
+ {0x8006, 66},
+ {0x800a, 66},
+ {0x800f, 66},
+ {0x8018, 66},
+ {0x801f, 66},
+ {0x8029, 66},
+ {0xc038, 66},
+ },
+ /* 39 */
+ {
+ {0x8003, 67},
+ {0x8006, 67},
+ {0x800a, 67},
+ {0x800f, 67},
+ {0x8018, 67},
+ {0x801f, 67},
+ {0x8029, 67},
+ {0xc038, 67},
+ {0x8003, 68},
+ {0x8006, 68},
+ {0x800a, 68},
+ {0x800f, 68},
+ {0x8018, 68},
+ {0x801f, 68},
+ {0x8029, 68},
+ {0xc038, 68},
+ },
+ /* 40 */
+ {
+ {0x2c, 0},
+ {0x2d, 0},
+ {0x2f, 0},
+ {0x30, 0},
+ {0x33, 0},
+ {0x34, 0},
+ {0x36, 0},
+ {0x37, 0},
+ {0x3b, 0},
+ {0x3c, 0},
+ {0x3e, 0},
+ {0x3f, 0},
+ {0x42, 0},
+ {0x43, 0},
+ {0x45, 0},
+ {0x4048, 0},
+ },
+ /* 41 */
+ {
+ {0xc000, 69},
+ {0xc000, 70},
+ {0xc000, 71},
+ {0xc000, 72},
+ {0xc000, 73},
+ {0xc000, 74},
+ {0xc000, 75},
+ {0xc000, 76},
+ {0xc000, 77},
+ {0xc000, 78},
+ {0xc000, 79},
+ {0xc000, 80},
+ {0xc000, 81},
+ {0xc000, 82},
+ {0xc000, 83},
+ {0xc000, 84},
+ },
+ /* 42 */
+ {
+ {0x8001, 69},
+ {0xc016, 69},
+ {0x8001, 70},
+ {0xc016, 70},
+ {0x8001, 71},
+ {0xc016, 71},
+ {0x8001, 72},
+ {0xc016, 72},
+ {0x8001, 73},
+ {0xc016, 73},
+ {0x8001, 74},
+ {0xc016, 74},
+ {0x8001, 75},
+ {0xc016, 75},
+ {0x8001, 76},
+ {0xc016, 76},
+ },
+ /* 43 */
+ {
+ {0x8002, 69},
+ {0x8009, 69},
+ {0x8017, 69},
+ {0xc028, 69},
+ {0x8002, 70},
+ {0x8009, 70},
+ {0x8017, 70},
+ {0xc028, 70},
+ {0x8002, 71},
+ {0x8009, 71},
+ {0x8017, 71},
+ {0xc028, 71},
+ {0x8002, 72},
+ {0x8009, 72},
+ {0x8017, 72},
+ {0xc028, 72},
+ },
+ /* 44 */
+ {
+ {0x8003, 69},
+ {0x8006, 69},
+ {0x800a, 69},
+ {0x800f, 69},
+ {0x8018, 69},
+ {0x801f, 69},
+ {0x8029, 69},
+ {0xc038, 69},
+ {0x8003, 70},
+ {0x8006, 70},
+ {0x800a, 70},
+ {0x800f, 70},
+ {0x8018, 70},
+ {0x801f, 70},
+ {0x8029, 70},
+ {0xc038, 70},
+ },
+ /* 45 */
+ {
+ {0x8003, 71},
+ {0x8006, 71},
+ {0x800a, 71},
+ {0x800f, 71},
+ {0x8018, 71},
+ {0x801f, 71},
+ {0x8029, 71},
+ {0xc038, 71},
+ {0x8003, 72},
+ {0x8006, 72},
+ {0x800a, 72},
+ {0x800f, 72},
+ {0x8018, 72},
+ {0x801f, 72},
+ {0x8029, 72},
+ {0xc038, 72},
+ },
+ /* 46 */
+ {
+ {0x8002, 73},
+ {0x8009, 73},
+ {0x8017, 73},
+ {0xc028, 73},
+ {0x8002, 74},
+ {0x8009, 74},
+ {0x8017, 74},
+ {0xc028, 74},
+ {0x8002, 75},
+ {0x8009, 75},
+ {0x8017, 75},
+ {0xc028, 75},
+ {0x8002, 76},
+ {0x8009, 76},
+ {0x8017, 76},
+ {0xc028, 76},
+ },
+ /* 47 */
+ {
+ {0x8003, 73},
+ {0x8006, 73},
+ {0x800a, 73},
+ {0x800f, 73},
+ {0x8018, 73},
+ {0x801f, 73},
+ {0x8029, 73},
+ {0xc038, 73},
+ {0x8003, 74},
+ {0x8006, 74},
+ {0x800a, 74},
+ {0x800f, 74},
+ {0x8018, 74},
+ {0x801f, 74},
+ {0x8029, 74},
+ {0xc038, 74},
+ },
+ /* 48 */
+ {
+ {0x8003, 75},
+ {0x8006, 75},
+ {0x800a, 75},
+ {0x800f, 75},
+ {0x8018, 75},
+ {0x801f, 75},
+ {0x8029, 75},
+ {0xc038, 75},
+ {0x8003, 76},
+ {0x8006, 76},
+ {0x800a, 76},
+ {0x800f, 76},
+ {0x8018, 76},
+ {0x801f, 76},
+ {0x8029, 76},
+ {0xc038, 76},
+ },
+ /* 49 */
+ {
+ {0x8001, 77},
+ {0xc016, 77},
+ {0x8001, 78},
+ {0xc016, 78},
+ {0x8001, 79},
+ {0xc016, 79},
+ {0x8001, 80},
+ {0xc016, 80},
+ {0x8001, 81},
+ {0xc016, 81},
+ {0x8001, 82},
+ {0xc016, 82},
+ {0x8001, 83},
+ {0xc016, 83},
+ {0x8001, 84},
+ {0xc016, 84},
+ },
+ /* 50 */
+ {
+ {0x8002, 77},
+ {0x8009, 77},
+ {0x8017, 77},
+ {0xc028, 77},
+ {0x8002, 78},
+ {0x8009, 78},
+ {0x8017, 78},
+ {0xc028, 78},
+ {0x8002, 79},
+ {0x8009, 79},
+ {0x8017, 79},
+ {0xc028, 79},
+ {0x8002, 80},
+ {0x8009, 80},
+ {0x8017, 80},
+ {0xc028, 80},
+ },
+ /* 51 */
+ {
+ {0x8003, 77},
+ {0x8006, 77},
+ {0x800a, 77},
+ {0x800f, 77},
+ {0x8018, 77},
+ {0x801f, 77},
+ {0x8029, 77},
+ {0xc038, 77},
+ {0x8003, 78},
+ {0x8006, 78},
+ {0x800a, 78},
+ {0x800f, 78},
+ {0x8018, 78},
+ {0x801f, 78},
+ {0x8029, 78},
+ {0xc038, 78},
+ },
+ /* 52 */
+ {
+ {0x8003, 79},
+ {0x8006, 79},
+ {0x800a, 79},
+ {0x800f, 79},
+ {0x8018, 79},
+ {0x801f, 79},
+ {0x8029, 79},
+ {0xc038, 79},
+ {0x8003, 80},
+ {0x8006, 80},
+ {0x800a, 80},
+ {0x800f, 80},
+ {0x8018, 80},
+ {0x801f, 80},
+ {0x8029, 80},
+ {0xc038, 80},
+ },
+ /* 53 */
+ {
+ {0x8002, 81},
+ {0x8009, 81},
+ {0x8017, 81},
+ {0xc028, 81},
+ {0x8002, 82},
+ {0x8009, 82},
+ {0x8017, 82},
+ {0xc028, 82},
+ {0x8002, 83},
+ {0x8009, 83},
+ {0x8017, 83},
+ {0xc028, 83},
+ {0x8002, 84},
+ {0x8009, 84},
+ {0x8017, 84},
+ {0xc028, 84},
+ },
+ /* 54 */
+ {
+ {0x8003, 81},
+ {0x8006, 81},
+ {0x800a, 81},
+ {0x800f, 81},
+ {0x8018, 81},
+ {0x801f, 81},
+ {0x8029, 81},
+ {0xc038, 81},
+ {0x8003, 82},
+ {0x8006, 82},
+ {0x800a, 82},
+ {0x800f, 82},
+ {0x8018, 82},
+ {0x801f, 82},
+ {0x8029, 82},
+ {0xc038, 82},
+ },
+ /* 55 */
+ {
+ {0x8003, 83},
+ {0x8006, 83},
+ {0x800a, 83},
+ {0x800f, 83},
+ {0x8018, 83},
+ {0x801f, 83},
+ {0x8029, 83},
+ {0xc038, 83},
+ {0x8003, 84},
+ {0x8006, 84},
+ {0x800a, 84},
+ {0x800f, 84},
+ {0x8018, 84},
+ {0x801f, 84},
+ {0x8029, 84},
+ {0xc038, 84},
+ },
+ /* 56 */
+ {
+ {0xc000, 85},
+ {0xc000, 86},
+ {0xc000, 87},
+ {0xc000, 89},
+ {0xc000, 106},
+ {0xc000, 107},
+ {0xc000, 113},
+ {0xc000, 118},
+ {0xc000, 119},
+ {0xc000, 120},
+ {0xc000, 121},
+ {0xc000, 122},
+ {0x46, 0},
+ {0x47, 0},
+ {0x49, 0},
+ {0x404a, 0},
+ },
+ /* 57 */
+ {
+ {0x8001, 85},
+ {0xc016, 85},
+ {0x8001, 86},
+ {0xc016, 86},
+ {0x8001, 87},
+ {0xc016, 87},
+ {0x8001, 89},
+ {0xc016, 89},
+ {0x8001, 106},
+ {0xc016, 106},
+ {0x8001, 107},
+ {0xc016, 107},
+ {0x8001, 113},
+ {0xc016, 113},
+ {0x8001, 118},
+ {0xc016, 118},
+ },
+ /* 58 */
+ {
+ {0x8002, 85},
+ {0x8009, 85},
+ {0x8017, 85},
+ {0xc028, 85},
+ {0x8002, 86},
+ {0x8009, 86},
+ {0x8017, 86},
+ {0xc028, 86},
+ {0x8002, 87},
+ {0x8009, 87},
+ {0x8017, 87},
+ {0xc028, 87},
+ {0x8002, 89},
+ {0x8009, 89},
+ {0x8017, 89},
+ {0xc028, 89},
+ },
+ /* 59 */
+ {
+ {0x8003, 85},
+ {0x8006, 85},
+ {0x800a, 85},
+ {0x800f, 85},
+ {0x8018, 85},
+ {0x801f, 85},
+ {0x8029, 85},
+ {0xc038, 85},
+ {0x8003, 86},
+ {0x8006, 86},
+ {0x800a, 86},
+ {0x800f, 86},
+ {0x8018, 86},
+ {0x801f, 86},
+ {0x8029, 86},
+ {0xc038, 86},
+ },
+ /* 60 */
+ {
+ {0x8003, 87},
+ {0x8006, 87},
+ {0x800a, 87},
+ {0x800f, 87},
+ {0x8018, 87},
+ {0x801f, 87},
+ {0x8029, 87},
+ {0xc038, 87},
+ {0x8003, 89},
+ {0x8006, 89},
+ {0x800a, 89},
+ {0x800f, 89},
+ {0x8018, 89},
+ {0x801f, 89},
+ {0x8029, 89},
+ {0xc038, 89},
+ },
+ /* 61 */
+ {
+ {0x8002, 106},
+ {0x8009, 106},
+ {0x8017, 106},
+ {0xc028, 106},
+ {0x8002, 107},
+ {0x8009, 107},
+ {0x8017, 107},
+ {0xc028, 107},
+ {0x8002, 113},
+ {0x8009, 113},
+ {0x8017, 113},
+ {0xc028, 113},
+ {0x8002, 118},
+ {0x8009, 118},
+ {0x8017, 118},
+ {0xc028, 118},
+ },
+ /* 62 */
+ {
+ {0x8003, 106},
+ {0x8006, 106},
+ {0x800a, 106},
+ {0x800f, 106},
+ {0x8018, 106},
+ {0x801f, 106},
+ {0x8029, 106},
+ {0xc038, 106},
+ {0x8003, 107},
+ {0x8006, 107},
+ {0x800a, 107},
+ {0x800f, 107},
+ {0x8018, 107},
+ {0x801f, 107},
+ {0x8029, 107},
+ {0xc038, 107},
+ },
+ /* 63 */
+ {
+ {0x8003, 113},
+ {0x8006, 113},
+ {0x800a, 113},
+ {0x800f, 113},
+ {0x8018, 113},
+ {0x801f, 113},
+ {0x8029, 113},
+ {0xc038, 113},
+ {0x8003, 118},
+ {0x8006, 118},
+ {0x800a, 118},
+ {0x800f, 118},
+ {0x8018, 118},
+ {0x801f, 118},
+ {0x8029, 118},
+ {0xc038, 118},
+ },
+ /* 64 */
+ {
+ {0x8001, 119},
+ {0xc016, 119},
+ {0x8001, 120},
+ {0xc016, 120},
+ {0x8001, 121},
+ {0xc016, 121},
+ {0x8001, 122},
+ {0xc016, 122},
+ {0xc000, 38},
+ {0xc000, 42},
+ {0xc000, 44},
+ {0xc000, 59},
+ {0xc000, 88},
+ {0xc000, 90},
+ {0x4b, 0},
+ {0x4e, 0},
+ },
+ /* 65 */
+ {
+ {0x8002, 119},
+ {0x8009, 119},
+ {0x8017, 119},
+ {0xc028, 119},
+ {0x8002, 120},
+ {0x8009, 120},
+ {0x8017, 120},
+ {0xc028, 120},
+ {0x8002, 121},
+ {0x8009, 121},
+ {0x8017, 121},
+ {0xc028, 121},
+ {0x8002, 122},
+ {0x8009, 122},
+ {0x8017, 122},
+ {0xc028, 122},
+ },
+ /* 66 */
+ {
+ {0x8003, 119},
+ {0x8006, 119},
+ {0x800a, 119},
+ {0x800f, 119},
+ {0x8018, 119},
+ {0x801f, 119},
+ {0x8029, 119},
+ {0xc038, 119},
+ {0x8003, 120},
+ {0x8006, 120},
+ {0x800a, 120},
+ {0x800f, 120},
+ {0x8018, 120},
+ {0x801f, 120},
+ {0x8029, 120},
+ {0xc038, 120},
+ },
+ /* 67 */
+ {
+ {0x8003, 121},
+ {0x8006, 121},
+ {0x800a, 121},
+ {0x800f, 121},
+ {0x8018, 121},
+ {0x801f, 121},
+ {0x8029, 121},
+ {0xc038, 121},
+ {0x8003, 122},
+ {0x8006, 122},
+ {0x800a, 122},
+ {0x800f, 122},
+ {0x8018, 122},
+ {0x801f, 122},
+ {0x8029, 122},
+ {0xc038, 122},
+ },
+ /* 68 */
+ {
+ {0x8001, 38},
+ {0xc016, 38},
+ {0x8001, 42},
+ {0xc016, 42},
+ {0x8001, 44},
+ {0xc016, 44},
+ {0x8001, 59},
+ {0xc016, 59},
+ {0x8001, 88},
+ {0xc016, 88},
+ {0x8001, 90},
+ {0xc016, 90},
+ {0x4c, 0},
+ {0x4d, 0},
+ {0x4f, 0},
+ {0x51, 0},
+ },
+ /* 69 */
+ {
+ {0x8002, 38},
+ {0x8009, 38},
+ {0x8017, 38},
+ {0xc028, 38},
+ {0x8002, 42},
+ {0x8009, 42},
+ {0x8017, 42},
+ {0xc028, 42},
+ {0x8002, 44},
+ {0x8009, 44},
+ {0x8017, 44},
+ {0xc028, 44},
+ {0x8002, 59},
+ {0x8009, 59},
+ {0x8017, 59},
+ {0xc028, 59},
+ },
+ /* 70 */
+ {
+ {0x8003, 38},
+ {0x8006, 38},
+ {0x800a, 38},
+ {0x800f, 38},
+ {0x8018, 38},
+ {0x801f, 38},
+ {0x8029, 38},
+ {0xc038, 38},
+ {0x8003, 42},
+ {0x8006, 42},
+ {0x800a, 42},
+ {0x800f, 42},
+ {0x8018, 42},
+ {0x801f, 42},
+ {0x8029, 42},
+ {0xc038, 42},
+ },
+ /* 71 */
+ {
+ {0x8003, 44},
+ {0x8006, 44},
+ {0x800a, 44},
+ {0x800f, 44},
+ {0x8018, 44},
+ {0x801f, 44},
+ {0x8029, 44},
+ {0xc038, 44},
+ {0x8003, 59},
+ {0x8006, 59},
+ {0x800a, 59},
+ {0x800f, 59},
+ {0x8018, 59},
+ {0x801f, 59},
+ {0x8029, 59},
+ {0xc038, 59},
+ },
+ /* 72 */
+ {
+ {0x8002, 88},
+ {0x8009, 88},
+ {0x8017, 88},
+ {0xc028, 88},
+ {0x8002, 90},
+ {0x8009, 90},
+ {0x8017, 90},
+ {0xc028, 90},
+ {0xc000, 33},
+ {0xc000, 34},
+ {0xc000, 40},
+ {0xc000, 41},
+ {0xc000, 63},
+ {0x50, 0},
+ {0x52, 0},
+ {0x54, 0},
+ },
+ /* 73 */
+ {
+ {0x8003, 88},
+ {0x8006, 88},
+ {0x800a, 88},
+ {0x800f, 88},
+ {0x8018, 88},
+ {0x801f, 88},
+ {0x8029, 88},
+ {0xc038, 88},
+ {0x8003, 90},
+ {0x8006, 90},
+ {0x800a, 90},
+ {0x800f, 90},
+ {0x8018, 90},
+ {0x801f, 90},
+ {0x8029, 90},
+ {0xc038, 90},
+ },
+ /* 74 */
+ {
+ {0x8001, 33},
+ {0xc016, 33},
+ {0x8001, 34},
+ {0xc016, 34},
+ {0x8001, 40},
+ {0xc016, 40},
+ {0x8001, 41},
+ {0xc016, 41},
+ {0x8001, 63},
+ {0xc016, 63},
+ {0xc000, 39},
+ {0xc000, 43},
+ {0xc000, 124},
+ {0x53, 0},
+ {0x55, 0},
+ {0x58, 0},
+ },
+ /* 75 */
+ {
+ {0x8002, 33},
+ {0x8009, 33},
+ {0x8017, 33},
+ {0xc028, 33},
+ {0x8002, 34},
+ {0x8009, 34},
+ {0x8017, 34},
+ {0xc028, 34},
+ {0x8002, 40},
+ {0x8009, 40},
+ {0x8017, 40},
+ {0xc028, 40},
+ {0x8002, 41},
+ {0x8009, 41},
+ {0x8017, 41},
+ {0xc028, 41},
+ },
+ /* 76 */
+ {
+ {0x8003, 33},
+ {0x8006, 33},
+ {0x800a, 33},
+ {0x800f, 33},
+ {0x8018, 33},
+ {0x801f, 33},
+ {0x8029, 33},
+ {0xc038, 33},
+ {0x8003, 34},
+ {0x8006, 34},
+ {0x800a, 34},
+ {0x800f, 34},
+ {0x8018, 34},
+ {0x801f, 34},
+ {0x8029, 34},
+ {0xc038, 34},
+ },
+ /* 77 */
+ {
+ {0x8003, 40},
+ {0x8006, 40},
+ {0x800a, 40},
+ {0x800f, 40},
+ {0x8018, 40},
+ {0x801f, 40},
+ {0x8029, 40},
+ {0xc038, 40},
+ {0x8003, 41},
+ {0x8006, 41},
+ {0x800a, 41},
+ {0x800f, 41},
+ {0x8018, 41},
+ {0x801f, 41},
+ {0x8029, 41},
+ {0xc038, 41},
+ },
+ /* 78 */
+ {
+ {0x8002, 63},
+ {0x8009, 63},
+ {0x8017, 63},
+ {0xc028, 63},
+ {0x8001, 39},
+ {0xc016, 39},
+ {0x8001, 43},
+ {0xc016, 43},
+ {0x8001, 124},
+ {0xc016, 124},
+ {0xc000, 35},
+ {0xc000, 62},
+ {0x56, 0},
+ {0x57, 0},
+ {0x59, 0},
+ {0x5a, 0},
+ },
+ /* 79 */
+ {
+ {0x8003, 63},
+ {0x8006, 63},
+ {0x800a, 63},
+ {0x800f, 63},
+ {0x8018, 63},
+ {0x801f, 63},
+ {0x8029, 63},
+ {0xc038, 63},
+ {0x8002, 39},
+ {0x8009, 39},
+ {0x8017, 39},
+ {0xc028, 39},
+ {0x8002, 43},
+ {0x8009, 43},
+ {0x8017, 43},
+ {0xc028, 43},
+ },
+ /* 80 */
+ {
+ {0x8003, 39},
+ {0x8006, 39},
+ {0x800a, 39},
+ {0x800f, 39},
+ {0x8018, 39},
+ {0x801f, 39},
+ {0x8029, 39},
+ {0xc038, 39},
+ {0x8003, 43},
+ {0x8006, 43},
+ {0x800a, 43},
+ {0x800f, 43},
+ {0x8018, 43},
+ {0x801f, 43},
+ {0x8029, 43},
+ {0xc038, 43},
+ },
+ /* 81 */
+ {
+ {0x8002, 124},
+ {0x8009, 124},
+ {0x8017, 124},
+ {0xc028, 124},
+ {0x8001, 35},
+ {0xc016, 35},
+ {0x8001, 62},
+ {0xc016, 62},
+ {0xc000, 0},
+ {0xc000, 36},
+ {0xc000, 64},
+ {0xc000, 91},
+ {0xc000, 93},
+ {0xc000, 126},
+ {0x5b, 0},
+ {0x5c, 0},
+ },
+ /* 82 */
+ {
+ {0x8003, 124},
+ {0x8006, 124},
+ {0x800a, 124},
+ {0x800f, 124},
+ {0x8018, 124},
+ {0x801f, 124},
+ {0x8029, 124},
+ {0xc038, 124},
+ {0x8002, 35},
+ {0x8009, 35},
+ {0x8017, 35},
+ {0xc028, 35},
+ {0x8002, 62},
+ {0x8009, 62},
+ {0x8017, 62},
+ {0xc028, 62},
+ },
+ /* 83 */
+ {
+ {0x8003, 35},
+ {0x8006, 35},
+ {0x800a, 35},
+ {0x800f, 35},
+ {0x8018, 35},
+ {0x801f, 35},
+ {0x8029, 35},
+ {0xc038, 35},
+ {0x8003, 62},
+ {0x8006, 62},
+ {0x800a, 62},
+ {0x800f, 62},
+ {0x8018, 62},
+ {0x801f, 62},
+ {0x8029, 62},
+ {0xc038, 62},
+ },
+ /* 84 */
+ {
+ {0x8001, 0},
+ {0xc016, 0},
+ {0x8001, 36},
+ {0xc016, 36},
+ {0x8001, 64},
+ {0xc016, 64},
+ {0x8001, 91},
+ {0xc016, 91},
+ {0x8001, 93},
+ {0xc016, 93},
+ {0x8001, 126},
+ {0xc016, 126},
+ {0xc000, 94},
+ {0xc000, 125},
+ {0x5d, 0},
+ {0x5e, 0},
+ },
+ /* 85 */
+ {
+ {0x8002, 0},
+ {0x8009, 0},
+ {0x8017, 0},
+ {0xc028, 0},
+ {0x8002, 36},
+ {0x8009, 36},
+ {0x8017, 36},
+ {0xc028, 36},
+ {0x8002, 64},
+ {0x8009, 64},
+ {0x8017, 64},
+ {0xc028, 64},
+ {0x8002, 91},
+ {0x8009, 91},
+ {0x8017, 91},
+ {0xc028, 91},
+ },
+ /* 86 */
+ {
+ {0x8003, 0},
+ {0x8006, 0},
+ {0x800a, 0},
+ {0x800f, 0},
+ {0x8018, 0},
+ {0x801f, 0},
+ {0x8029, 0},
+ {0xc038, 0},
+ {0x8003, 36},
+ {0x8006, 36},
+ {0x800a, 36},
+ {0x800f, 36},
+ {0x8018, 36},
+ {0x801f, 36},
+ {0x8029, 36},
+ {0xc038, 36},
+ },
+ /* 87 */
+ {
+ {0x8003, 64},
+ {0x8006, 64},
+ {0x800a, 64},
+ {0x800f, 64},
+ {0x8018, 64},
+ {0x801f, 64},
+ {0x8029, 64},
+ {0xc038, 64},
+ {0x8003, 91},
+ {0x8006, 91},
+ {0x800a, 91},
+ {0x800f, 91},
+ {0x8018, 91},
+ {0x801f, 91},
+ {0x8029, 91},
+ {0xc038, 91},
+ },
+ /* 88 */
+ {
+ {0x8002, 93},
+ {0x8009, 93},
+ {0x8017, 93},
+ {0xc028, 93},
+ {0x8002, 126},
+ {0x8009, 126},
+ {0x8017, 126},
+ {0xc028, 126},
+ {0x8001, 94},
+ {0xc016, 94},
+ {0x8001, 125},
+ {0xc016, 125},
+ {0xc000, 60},
+ {0xc000, 96},
+ {0xc000, 123},
+ {0x5f, 0},
+ },
+ /* 89 */
+ {
+ {0x8003, 93},
+ {0x8006, 93},
+ {0x800a, 93},
+ {0x800f, 93},
+ {0x8018, 93},
+ {0x801f, 93},
+ {0x8029, 93},
+ {0xc038, 93},
+ {0x8003, 126},
+ {0x8006, 126},
+ {0x800a, 126},
+ {0x800f, 126},
+ {0x8018, 126},
+ {0x801f, 126},
+ {0x8029, 126},
+ {0xc038, 126},
+ },
+ /* 90 */
+ {
+ {0x8002, 94},
+ {0x8009, 94},
+ {0x8017, 94},
+ {0xc028, 94},
+ {0x8002, 125},
+ {0x8009, 125},
+ {0x8017, 125},
+ {0xc028, 125},
+ {0x8001, 60},
+ {0xc016, 60},
+ {0x8001, 96},
+ {0xc016, 96},
+ {0x8001, 123},
+ {0xc016, 123},
+ {0x60, 0},
+ {0x6e, 0},
+ },
+ /* 91 */
+ {
+ {0x8003, 94},
+ {0x8006, 94},
+ {0x800a, 94},
+ {0x800f, 94},
+ {0x8018, 94},
+ {0x801f, 94},
+ {0x8029, 94},
+ {0xc038, 94},
+ {0x8003, 125},
+ {0x8006, 125},
+ {0x800a, 125},
+ {0x800f, 125},
+ {0x8018, 125},
+ {0x801f, 125},
+ {0x8029, 125},
+ {0xc038, 125},
+ },
+ /* 92 */
+ {
+ {0x8002, 60},
+ {0x8009, 60},
+ {0x8017, 60},
+ {0xc028, 60},
+ {0x8002, 96},
+ {0x8009, 96},
+ {0x8017, 96},
+ {0xc028, 96},
+ {0x8002, 123},
+ {0x8009, 123},
+ {0x8017, 123},
+ {0xc028, 123},
+ {0x61, 0},
+ {0x65, 0},
+ {0x6f, 0},
+ {0x85, 0},
+ },
+ /* 93 */
+ {
+ {0x8003, 60},
+ {0x8006, 60},
+ {0x800a, 60},
+ {0x800f, 60},
+ {0x8018, 60},
+ {0x801f, 60},
+ {0x8029, 60},
+ {0xc038, 60},
+ {0x8003, 96},
+ {0x8006, 96},
+ {0x800a, 96},
+ {0x800f, 96},
+ {0x8018, 96},
+ {0x801f, 96},
+ {0x8029, 96},
+ {0xc038, 96},
+ },
+ /* 94 */
+ {
+ {0x8003, 123},
+ {0x8006, 123},
+ {0x800a, 123},
+ {0x800f, 123},
+ {0x8018, 123},
+ {0x801f, 123},
+ {0x8029, 123},
+ {0xc038, 123},
+ {0x62, 0},
+ {0x63, 0},
+ {0x66, 0},
+ {0x69, 0},
+ {0x70, 0},
+ {0x77, 0},
+ {0x86, 0},
+ {0x99, 0},
+ },
+ /* 95 */
+ {
+ {0xc000, 92},
+ {0xc000, 195},
+ {0xc000, 208},
+ {0x64, 0},
+ {0x67, 0},
+ {0x68, 0},
+ {0x6a, 0},
+ {0x6b, 0},
+ {0x71, 0},
+ {0x74, 0},
+ {0x78, 0},
+ {0x7e, 0},
+ {0x87, 0},
+ {0x8e, 0},
+ {0x9a, 0},
+ {0xa9, 0},
+ },
+ /* 96 */
+ {
+ {0x8001, 92},
+ {0xc016, 92},
+ {0x8001, 195},
+ {0xc016, 195},
+ {0x8001, 208},
+ {0xc016, 208},
+ {0xc000, 128},
+ {0xc000, 130},
+ {0xc000, 131},
+ {0xc000, 162},
+ {0xc000, 184},
+ {0xc000, 194},
+ {0xc000, 224},
+ {0xc000, 226},
+ {0x6c, 0},
+ {0x6d, 0},
+ },
+ /* 97 */
+ {
+ {0x8002, 92},
+ {0x8009, 92},
+ {0x8017, 92},
+ {0xc028, 92},
+ {0x8002, 195},
+ {0x8009, 195},
+ {0x8017, 195},
+ {0xc028, 195},
+ {0x8002, 208},
+ {0x8009, 208},
+ {0x8017, 208},
+ {0xc028, 208},
+ {0x8001, 128},
+ {0xc016, 128},
+ {0x8001, 130},
+ {0xc016, 130},
+ },
+ /* 98 */
+ {
+ {0x8003, 92},
+ {0x8006, 92},
+ {0x800a, 92},
+ {0x800f, 92},
+ {0x8018, 92},
+ {0x801f, 92},
+ {0x8029, 92},
+ {0xc038, 92},
+ {0x8003, 195},
+ {0x8006, 195},
+ {0x800a, 195},
+ {0x800f, 195},
+ {0x8018, 195},
+ {0x801f, 195},
+ {0x8029, 195},
+ {0xc038, 195},
+ },
+ /* 99 */
+ {
+ {0x8003, 208},
+ {0x8006, 208},
+ {0x800a, 208},
+ {0x800f, 208},
+ {0x8018, 208},
+ {0x801f, 208},
+ {0x8029, 208},
+ {0xc038, 208},
+ {0x8002, 128},
+ {0x8009, 128},
+ {0x8017, 128},
+ {0xc028, 128},
+ {0x8002, 130},
+ {0x8009, 130},
+ {0x8017, 130},
+ {0xc028, 130},
+ },
+ /* 100 */
+ {
+ {0x8003, 128},
+ {0x8006, 128},
+ {0x800a, 128},
+ {0x800f, 128},
+ {0x8018, 128},
+ {0x801f, 128},
+ {0x8029, 128},
+ {0xc038, 128},
+ {0x8003, 130},
+ {0x8006, 130},
+ {0x800a, 130},
+ {0x800f, 130},
+ {0x8018, 130},
+ {0x801f, 130},
+ {0x8029, 130},
+ {0xc038, 130},
+ },
+ /* 101 */
+ {
+ {0x8001, 131},
+ {0xc016, 131},
+ {0x8001, 162},
+ {0xc016, 162},
+ {0x8001, 184},
+ {0xc016, 184},
+ {0x8001, 194},
+ {0xc016, 194},
+ {0x8001, 224},
+ {0xc016, 224},
+ {0x8001, 226},
+ {0xc016, 226},
+ {0xc000, 153},
+ {0xc000, 161},
+ {0xc000, 167},
+ {0xc000, 172},
+ },
+ /* 102 */
+ {
+ {0x8002, 131},
+ {0x8009, 131},
+ {0x8017, 131},
+ {0xc028, 131},
+ {0x8002, 162},
+ {0x8009, 162},
+ {0x8017, 162},
+ {0xc028, 162},
+ {0x8002, 184},
+ {0x8009, 184},
+ {0x8017, 184},
+ {0xc028, 184},
+ {0x8002, 194},
+ {0x8009, 194},
+ {0x8017, 194},
+ {0xc028, 194},
+ },
+ /* 103 */
+ {
+ {0x8003, 131},
+ {0x8006, 131},
+ {0x800a, 131},
+ {0x800f, 131},
+ {0x8018, 131},
+ {0x801f, 131},
+ {0x8029, 131},
+ {0xc038, 131},
+ {0x8003, 162},
+ {0x8006, 162},
+ {0x800a, 162},
+ {0x800f, 162},
+ {0x8018, 162},
+ {0x801f, 162},
+ {0x8029, 162},
+ {0xc038, 162},
+ },
+ /* 104 */
+ {
+ {0x8003, 184},
+ {0x8006, 184},
+ {0x800a, 184},
+ {0x800f, 184},
+ {0x8018, 184},
+ {0x801f, 184},
+ {0x8029, 184},
+ {0xc038, 184},
+ {0x8003, 194},
+ {0x8006, 194},
+ {0x800a, 194},
+ {0x800f, 194},
+ {0x8018, 194},
+ {0x801f, 194},
+ {0x8029, 194},
+ {0xc038, 194},
+ },
+ /* 105 */
+ {
+ {0x8002, 224},
+ {0x8009, 224},
+ {0x8017, 224},
+ {0xc028, 224},
+ {0x8002, 226},
+ {0x8009, 226},
+ {0x8017, 226},
+ {0xc028, 226},
+ {0x8001, 153},
+ {0xc016, 153},
+ {0x8001, 161},
+ {0xc016, 161},
+ {0x8001, 167},
+ {0xc016, 167},
+ {0x8001, 172},
+ {0xc016, 172},
+ },
+ /* 106 */
+ {
+ {0x8003, 224},
+ {0x8006, 224},
+ {0x800a, 224},
+ {0x800f, 224},
+ {0x8018, 224},
+ {0x801f, 224},
+ {0x8029, 224},
+ {0xc038, 224},
+ {0x8003, 226},
+ {0x8006, 226},
+ {0x800a, 226},
+ {0x800f, 226},
+ {0x8018, 226},
+ {0x801f, 226},
+ {0x8029, 226},
+ {0xc038, 226},
+ },
+ /* 107 */
+ {
+ {0x8002, 153},
+ {0x8009, 153},
+ {0x8017, 153},
+ {0xc028, 153},
+ {0x8002, 161},
+ {0x8009, 161},
+ {0x8017, 161},
+ {0xc028, 161},
+ {0x8002, 167},
+ {0x8009, 167},
+ {0x8017, 167},
+ {0xc028, 167},
+ {0x8002, 172},
+ {0x8009, 172},
+ {0x8017, 172},
+ {0xc028, 172},
+ },
+ /* 108 */
+ {
+ {0x8003, 153},
+ {0x8006, 153},
+ {0x800a, 153},
+ {0x800f, 153},
+ {0x8018, 153},
+ {0x801f, 153},
+ {0x8029, 153},
+ {0xc038, 153},
+ {0x8003, 161},
+ {0x8006, 161},
+ {0x800a, 161},
+ {0x800f, 161},
+ {0x8018, 161},
+ {0x801f, 161},
+ {0x8029, 161},
+ {0xc038, 161},
+ },
+ /* 109 */
+ {
+ {0x8003, 167},
+ {0x8006, 167},
+ {0x800a, 167},
+ {0x800f, 167},
+ {0x8018, 167},
+ {0x801f, 167},
+ {0x8029, 167},
+ {0xc038, 167},
+ {0x8003, 172},
+ {0x8006, 172},
+ {0x800a, 172},
+ {0x800f, 172},
+ {0x8018, 172},
+ {0x801f, 172},
+ {0x8029, 172},
+ {0xc038, 172},
+ },
+ /* 110 */
+ {
+ {0x72, 0},
+ {0x73, 0},
+ {0x75, 0},
+ {0x76, 0},
+ {0x79, 0},
+ {0x7b, 0},
+ {0x7f, 0},
+ {0x82, 0},
+ {0x88, 0},
+ {0x8b, 0},
+ {0x8f, 0},
+ {0x92, 0},
+ {0x9b, 0},
+ {0xa2, 0},
+ {0xaa, 0},
+ {0xb4, 0},
+ },
+ /* 111 */
+ {
+ {0xc000, 176},
+ {0xc000, 177},
+ {0xc000, 179},
+ {0xc000, 209},
+ {0xc000, 216},
+ {0xc000, 217},
+ {0xc000, 227},
+ {0xc000, 229},
+ {0xc000, 230},
+ {0x7a, 0},
+ {0x7c, 0},
+ {0x7d, 0},
+ {0x80, 0},
+ {0x81, 0},
+ {0x83, 0},
+ {0x84, 0},
+ },
+ /* 112 */
+ {
+ {0x8001, 176},
+ {0xc016, 176},
+ {0x8001, 177},
+ {0xc016, 177},
+ {0x8001, 179},
+ {0xc016, 179},
+ {0x8001, 209},
+ {0xc016, 209},
+ {0x8001, 216},
+ {0xc016, 216},
+ {0x8001, 217},
+ {0xc016, 217},
+ {0x8001, 227},
+ {0xc016, 227},
+ {0x8001, 229},
+ {0xc016, 229},
+ },
+ /* 113 */
+ {
+ {0x8002, 176},
+ {0x8009, 176},
+ {0x8017, 176},
+ {0xc028, 176},
+ {0x8002, 177},
+ {0x8009, 177},
+ {0x8017, 177},
+ {0xc028, 177},
+ {0x8002, 179},
+ {0x8009, 179},
+ {0x8017, 179},
+ {0xc028, 179},
+ {0x8002, 209},
+ {0x8009, 209},
+ {0x8017, 209},
+ {0xc028, 209},
+ },
+ /* 114 */
+ {
+ {0x8003, 176},
+ {0x8006, 176},
+ {0x800a, 176},
+ {0x800f, 176},
+ {0x8018, 176},
+ {0x801f, 176},
+ {0x8029, 176},
+ {0xc038, 176},
+ {0x8003, 177},
+ {0x8006, 177},
+ {0x800a, 177},
+ {0x800f, 177},
+ {0x8018, 177},
+ {0x801f, 177},
+ {0x8029, 177},
+ {0xc038, 177},
+ },
+ /* 115 */
+ {
+ {0x8003, 179},
+ {0x8006, 179},
+ {0x800a, 179},
+ {0x800f, 179},
+ {0x8018, 179},
+ {0x801f, 179},
+ {0x8029, 179},
+ {0xc038, 179},
+ {0x8003, 209},
+ {0x8006, 209},
+ {0x800a, 209},
+ {0x800f, 209},
+ {0x8018, 209},
+ {0x801f, 209},
+ {0x8029, 209},
+ {0xc038, 209},
+ },
+ /* 116 */
+ {
+ {0x8002, 216},
+ {0x8009, 216},
+ {0x8017, 216},
+ {0xc028, 216},
+ {0x8002, 217},
+ {0x8009, 217},
+ {0x8017, 217},
+ {0xc028, 217},
+ {0x8002, 227},
+ {0x8009, 227},
+ {0x8017, 227},
+ {0xc028, 227},
+ {0x8002, 229},
+ {0x8009, 229},
+ {0x8017, 229},
+ {0xc028, 229},
+ },
+ /* 117 */
+ {
+ {0x8003, 216},
+ {0x8006, 216},
+ {0x800a, 216},
+ {0x800f, 216},
+ {0x8018, 216},
+ {0x801f, 216},
+ {0x8029, 216},
+ {0xc038, 216},
+ {0x8003, 217},
+ {0x8006, 217},
+ {0x800a, 217},
+ {0x800f, 217},
+ {0x8018, 217},
+ {0x801f, 217},
+ {0x8029, 217},
+ {0xc038, 217},
+ },
+ /* 118 */
+ {
+ {0x8003, 227},
+ {0x8006, 227},
+ {0x800a, 227},
+ {0x800f, 227},
+ {0x8018, 227},
+ {0x801f, 227},
+ {0x8029, 227},
+ {0xc038, 227},
+ {0x8003, 229},
+ {0x8006, 229},
+ {0x800a, 229},
+ {0x800f, 229},
+ {0x8018, 229},
+ {0x801f, 229},
+ {0x8029, 229},
+ {0xc038, 229},
+ },
+ /* 119 */
+ {
+ {0x8001, 230},
+ {0xc016, 230},
+ {0xc000, 129},
+ {0xc000, 132},
+ {0xc000, 133},
+ {0xc000, 134},
+ {0xc000, 136},
+ {0xc000, 146},
+ {0xc000, 154},
+ {0xc000, 156},
+ {0xc000, 160},
+ {0xc000, 163},
+ {0xc000, 164},
+ {0xc000, 169},
+ {0xc000, 170},
+ {0xc000, 173},
+ },
+ /* 120 */
+ {
+ {0x8002, 230},
+ {0x8009, 230},
+ {0x8017, 230},
+ {0xc028, 230},
+ {0x8001, 129},
+ {0xc016, 129},
+ {0x8001, 132},
+ {0xc016, 132},
+ {0x8001, 133},
+ {0xc016, 133},
+ {0x8001, 134},
+ {0xc016, 134},
+ {0x8001, 136},
+ {0xc016, 136},
+ {0x8001, 146},
+ {0xc016, 146},
+ },
+ /* 121 */
+ {
+ {0x8003, 230},
+ {0x8006, 230},
+ {0x800a, 230},
+ {0x800f, 230},
+ {0x8018, 230},
+ {0x801f, 230},
+ {0x8029, 230},
+ {0xc038, 230},
+ {0x8002, 129},
+ {0x8009, 129},
+ {0x8017, 129},
+ {0xc028, 129},
+ {0x8002, 132},
+ {0x8009, 132},
+ {0x8017, 132},
+ {0xc028, 132},
+ },
+ /* 122 */
+ {
+ {0x8003, 129},
+ {0x8006, 129},
+ {0x800a, 129},
+ {0x800f, 129},
+ {0x8018, 129},
+ {0x801f, 129},
+ {0x8029, 129},
+ {0xc038, 129},
+ {0x8003, 132},
+ {0x8006, 132},
+ {0x800a, 132},
+ {0x800f, 132},
+ {0x8018, 132},
+ {0x801f, 132},
+ {0x8029, 132},
+ {0xc038, 132},
+ },
+ /* 123 */
+ {
+ {0x8002, 133},
+ {0x8009, 133},
+ {0x8017, 133},
+ {0xc028, 133},
+ {0x8002, 134},
+ {0x8009, 134},
+ {0x8017, 134},
+ {0xc028, 134},
+ {0x8002, 136},
+ {0x8009, 136},
+ {0x8017, 136},
+ {0xc028, 136},
+ {0x8002, 146},
+ {0x8009, 146},
+ {0x8017, 146},
+ {0xc028, 146},
+ },
+ /* 124 */
+ {
+ {0x8003, 133},
+ {0x8006, 133},
+ {0x800a, 133},
+ {0x800f, 133},
+ {0x8018, 133},
+ {0x801f, 133},
+ {0x8029, 133},
+ {0xc038, 133},
+ {0x8003, 134},
+ {0x8006, 134},
+ {0x800a, 134},
+ {0x800f, 134},
+ {0x8018, 134},
+ {0x801f, 134},
+ {0x8029, 134},
+ {0xc038, 134},
+ },
+ /* 125 */
+ {
+ {0x8003, 136},
+ {0x8006, 136},
+ {0x800a, 136},
+ {0x800f, 136},
+ {0x8018, 136},
+ {0x801f, 136},
+ {0x8029, 136},
+ {0xc038, 136},
+ {0x8003, 146},
+ {0x8006, 146},
+ {0x800a, 146},
+ {0x800f, 146},
+ {0x8018, 146},
+ {0x801f, 146},
+ {0x8029, 146},
+ {0xc038, 146},
+ },
+ /* 126 */
+ {
+ {0x8001, 154},
+ {0xc016, 154},
+ {0x8001, 156},
+ {0xc016, 156},
+ {0x8001, 160},
+ {0xc016, 160},
+ {0x8001, 163},
+ {0xc016, 163},
+ {0x8001, 164},
+ {0xc016, 164},
+ {0x8001, 169},
+ {0xc016, 169},
+ {0x8001, 170},
+ {0xc016, 170},
+ {0x8001, 173},
+ {0xc016, 173},
+ },
+ /* 127 */
+ {
+ {0x8002, 154},
+ {0x8009, 154},
+ {0x8017, 154},
+ {0xc028, 154},
+ {0x8002, 156},
+ {0x8009, 156},
+ {0x8017, 156},
+ {0xc028, 156},
+ {0x8002, 160},
+ {0x8009, 160},
+ {0x8017, 160},
+ {0xc028, 160},
+ {0x8002, 163},
+ {0x8009, 163},
+ {0x8017, 163},
+ {0xc028, 163},
+ },
+ /* 128 */
+ {
+ {0x8003, 154},
+ {0x8006, 154},
+ {0x800a, 154},
+ {0x800f, 154},
+ {0x8018, 154},
+ {0x801f, 154},
+ {0x8029, 154},
+ {0xc038, 154},
+ {0x8003, 156},
+ {0x8006, 156},
+ {0x800a, 156},
+ {0x800f, 156},
+ {0x8018, 156},
+ {0x801f, 156},
+ {0x8029, 156},
+ {0xc038, 156},
+ },
+ /* 129 */
+ {
+ {0x8003, 160},
+ {0x8006, 160},
+ {0x800a, 160},
+ {0x800f, 160},
+ {0x8018, 160},
+ {0x801f, 160},
+ {0x8029, 160},
+ {0xc038, 160},
+ {0x8003, 163},
+ {0x8006, 163},
+ {0x800a, 163},
+ {0x800f, 163},
+ {0x8018, 163},
+ {0x801f, 163},
+ {0x8029, 163},
+ {0xc038, 163},
+ },
+ /* 130 */
+ {
+ {0x8002, 164},
+ {0x8009, 164},
+ {0x8017, 164},
+ {0xc028, 164},
+ {0x8002, 169},
+ {0x8009, 169},
+ {0x8017, 169},
+ {0xc028, 169},
+ {0x8002, 170},
+ {0x8009, 170},
+ {0x8017, 170},
+ {0xc028, 170},
+ {0x8002, 173},
+ {0x8009, 173},
+ {0x8017, 173},
+ {0xc028, 173},
+ },
+ /* 131 */
+ {
+ {0x8003, 164},
+ {0x8006, 164},
+ {0x800a, 164},
+ {0x800f, 164},
+ {0x8018, 164},
+ {0x801f, 164},
+ {0x8029, 164},
+ {0xc038, 164},
+ {0x8003, 169},
+ {0x8006, 169},
+ {0x800a, 169},
+ {0x800f, 169},
+ {0x8018, 169},
+ {0x801f, 169},
+ {0x8029, 169},
+ {0xc038, 169},
+ },
+ /* 132 */
+ {
+ {0x8003, 170},
+ {0x8006, 170},
+ {0x800a, 170},
+ {0x800f, 170},
+ {0x8018, 170},
+ {0x801f, 170},
+ {0x8029, 170},
+ {0xc038, 170},
+ {0x8003, 173},
+ {0x8006, 173},
+ {0x800a, 173},
+ {0x800f, 173},
+ {0x8018, 173},
+ {0x801f, 173},
+ {0x8029, 173},
+ {0xc038, 173},
+ },
+ /* 133 */
+ {
+ {0x89, 0},
+ {0x8a, 0},
+ {0x8c, 0},
+ {0x8d, 0},
+ {0x90, 0},
+ {0x91, 0},
+ {0x93, 0},
+ {0x96, 0},
+ {0x9c, 0},
+ {0x9f, 0},
+ {0xa3, 0},
+ {0xa6, 0},
+ {0xab, 0},
+ {0xae, 0},
+ {0xb5, 0},
+ {0xbe, 0},
+ },
+ /* 134 */
+ {
+ {0xc000, 178},
+ {0xc000, 181},
+ {0xc000, 185},
+ {0xc000, 186},
+ {0xc000, 187},
+ {0xc000, 189},
+ {0xc000, 190},
+ {0xc000, 196},
+ {0xc000, 198},
+ {0xc000, 228},
+ {0xc000, 232},
+ {0xc000, 233},
+ {0x94, 0},
+ {0x95, 0},
+ {0x97, 0},
+ {0x98, 0},
+ },
+ /* 135 */
+ {
+ {0x8001, 178},
+ {0xc016, 178},
+ {0x8001, 181},
+ {0xc016, 181},
+ {0x8001, 185},
+ {0xc016, 185},
+ {0x8001, 186},
+ {0xc016, 186},
+ {0x8001, 187},
+ {0xc016, 187},
+ {0x8001, 189},
+ {0xc016, 189},
+ {0x8001, 190},
+ {0xc016, 190},
+ {0x8001, 196},
+ {0xc016, 196},
+ },
+ /* 136 */
+ {
+ {0x8002, 178},
+ {0x8009, 178},
+ {0x8017, 178},
+ {0xc028, 178},
+ {0x8002, 181},
+ {0x8009, 181},
+ {0x8017, 181},
+ {0xc028, 181},
+ {0x8002, 185},
+ {0x8009, 185},
+ {0x8017, 185},
+ {0xc028, 185},
+ {0x8002, 186},
+ {0x8009, 186},
+ {0x8017, 186},
+ {0xc028, 186},
+ },
+ /* 137 */
+ {
+ {0x8003, 178},
+ {0x8006, 178},
+ {0x800a, 178},
+ {0x800f, 178},
+ {0x8018, 178},
+ {0x801f, 178},
+ {0x8029, 178},
+ {0xc038, 178},
+ {0x8003, 181},
+ {0x8006, 181},
+ {0x800a, 181},
+ {0x800f, 181},
+ {0x8018, 181},
+ {0x801f, 181},
+ {0x8029, 181},
+ {0xc038, 181},
+ },
+ /* 138 */
+ {
+ {0x8003, 185},
+ {0x8006, 185},
+ {0x800a, 185},
+ {0x800f, 185},
+ {0x8018, 185},
+ {0x801f, 185},
+ {0x8029, 185},
+ {0xc038, 185},
+ {0x8003, 186},
+ {0x8006, 186},
+ {0x800a, 186},
+ {0x800f, 186},
+ {0x8018, 186},
+ {0x801f, 186},
+ {0x8029, 186},
+ {0xc038, 186},
+ },
+ /* 139 */
+ {
+ {0x8002, 187},
+ {0x8009, 187},
+ {0x8017, 187},
+ {0xc028, 187},
+ {0x8002, 189},
+ {0x8009, 189},
+ {0x8017, 189},
+ {0xc028, 189},
+ {0x8002, 190},
+ {0x8009, 190},
+ {0x8017, 190},
+ {0xc028, 190},
+ {0x8002, 196},
+ {0x8009, 196},
+ {0x8017, 196},
+ {0xc028, 196},
+ },
+ /* 140 */
+ {
+ {0x8003, 187},
+ {0x8006, 187},
+ {0x800a, 187},
+ {0x800f, 187},
+ {0x8018, 187},
+ {0x801f, 187},
+ {0x8029, 187},
+ {0xc038, 187},
+ {0x8003, 189},
+ {0x8006, 189},
+ {0x800a, 189},
+ {0x800f, 189},
+ {0x8018, 189},
+ {0x801f, 189},
+ {0x8029, 189},
+ {0xc038, 189},
+ },
+ /* 141 */
+ {
+ {0x8003, 190},
+ {0x8006, 190},
+ {0x800a, 190},
+ {0x800f, 190},
+ {0x8018, 190},
+ {0x801f, 190},
+ {0x8029, 190},
+ {0xc038, 190},
+ {0x8003, 196},
+ {0x8006, 196},
+ {0x800a, 196},
+ {0x800f, 196},
+ {0x8018, 196},
+ {0x801f, 196},
+ {0x8029, 196},
+ {0xc038, 196},
+ },
+ /* 142 */
+ {
+ {0x8001, 198},
+ {0xc016, 198},
+ {0x8001, 228},
+ {0xc016, 228},
+ {0x8001, 232},
+ {0xc016, 232},
+ {0x8001, 233},
+ {0xc016, 233},
+ {0xc000, 1},
+ {0xc000, 135},
+ {0xc000, 137},
+ {0xc000, 138},
+ {0xc000, 139},
+ {0xc000, 140},
+ {0xc000, 141},
+ {0xc000, 143},
+ },
+ /* 143 */
+ {
+ {0x8002, 198},
+ {0x8009, 198},
+ {0x8017, 198},
+ {0xc028, 198},
+ {0x8002, 228},
+ {0x8009, 228},
+ {0x8017, 228},
+ {0xc028, 228},
+ {0x8002, 232},
+ {0x8009, 232},
+ {0x8017, 232},
+ {0xc028, 232},
+ {0x8002, 233},
+ {0x8009, 233},
+ {0x8017, 233},
+ {0xc028, 233},
+ },
+ /* 144 */
+ {
+ {0x8003, 198},
+ {0x8006, 198},
+ {0x800a, 198},
+ {0x800f, 198},
+ {0x8018, 198},
+ {0x801f, 198},
+ {0x8029, 198},
+ {0xc038, 198},
+ {0x8003, 228},
+ {0x8006, 228},
+ {0x800a, 228},
+ {0x800f, 228},
+ {0x8018, 228},
+ {0x801f, 228},
+ {0x8029, 228},
+ {0xc038, 228},
+ },
+ /* 145 */
+ {
+ {0x8003, 232},
+ {0x8006, 232},
+ {0x800a, 232},
+ {0x800f, 232},
+ {0x8018, 232},
+ {0x801f, 232},
+ {0x8029, 232},
+ {0xc038, 232},
+ {0x8003, 233},
+ {0x8006, 233},
+ {0x800a, 233},
+ {0x800f, 233},
+ {0x8018, 233},
+ {0x801f, 233},
+ {0x8029, 233},
+ {0xc038, 233},
+ },
+ /* 146 */
+ {
+ {0x8001, 1},
+ {0xc016, 1},
+ {0x8001, 135},
+ {0xc016, 135},
+ {0x8001, 137},
+ {0xc016, 137},
+ {0x8001, 138},
+ {0xc016, 138},
+ {0x8001, 139},
+ {0xc016, 139},
+ {0x8001, 140},
+ {0xc016, 140},
+ {0x8001, 141},
+ {0xc016, 141},
+ {0x8001, 143},
+ {0xc016, 143},
+ },
+ /* 147 */
+ {
+ {0x8002, 1},
+ {0x8009, 1},
+ {0x8017, 1},
+ {0xc028, 1},
+ {0x8002, 135},
+ {0x8009, 135},
+ {0x8017, 135},
+ {0xc028, 135},
+ {0x8002, 137},
+ {0x8009, 137},
+ {0x8017, 137},
+ {0xc028, 137},
+ {0x8002, 138},
+ {0x8009, 138},
+ {0x8017, 138},
+ {0xc028, 138},
+ },
+ /* 148 */
+ {
+ {0x8003, 1},
+ {0x8006, 1},
+ {0x800a, 1},
+ {0x800f, 1},
+ {0x8018, 1},
+ {0x801f, 1},
+ {0x8029, 1},
+ {0xc038, 1},
+ {0x8003, 135},
+ {0x8006, 135},
+ {0x800a, 135},
+ {0x800f, 135},
+ {0x8018, 135},
+ {0x801f, 135},
+ {0x8029, 135},
+ {0xc038, 135},
+ },
+ /* 149 */
+ {
+ {0x8003, 137},
+ {0x8006, 137},
+ {0x800a, 137},
+ {0x800f, 137},
+ {0x8018, 137},
+ {0x801f, 137},
+ {0x8029, 137},
+ {0xc038, 137},
+ {0x8003, 138},
+ {0x8006, 138},
+ {0x800a, 138},
+ {0x800f, 138},
+ {0x8018, 138},
+ {0x801f, 138},
+ {0x8029, 138},
+ {0xc038, 138},
+ },
+ /* 150 */
+ {
+ {0x8002, 139},
+ {0x8009, 139},
+ {0x8017, 139},
+ {0xc028, 139},
+ {0x8002, 140},
+ {0x8009, 140},
+ {0x8017, 140},
+ {0xc028, 140},
+ {0x8002, 141},
+ {0x8009, 141},
+ {0x8017, 141},
+ {0xc028, 141},
+ {0x8002, 143},
+ {0x8009, 143},
+ {0x8017, 143},
+ {0xc028, 143},
+ },
+ /* 151 */
+ {
+ {0x8003, 139},
+ {0x8006, 139},
+ {0x800a, 139},
+ {0x800f, 139},
+ {0x8018, 139},
+ {0x801f, 139},
+ {0x8029, 139},
+ {0xc038, 139},
+ {0x8003, 140},
+ {0x8006, 140},
+ {0x800a, 140},
+ {0x800f, 140},
+ {0x8018, 140},
+ {0x801f, 140},
+ {0x8029, 140},
+ {0xc038, 140},
+ },
+ /* 152 */
+ {
+ {0x8003, 141},
+ {0x8006, 141},
+ {0x800a, 141},
+ {0x800f, 141},
+ {0x8018, 141},
+ {0x801f, 141},
+ {0x8029, 141},
+ {0xc038, 141},
+ {0x8003, 143},
+ {0x8006, 143},
+ {0x800a, 143},
+ {0x800f, 143},
+ {0x8018, 143},
+ {0x801f, 143},
+ {0x8029, 143},
+ {0xc038, 143},
+ },
+ /* 153 */
+ {
+ {0x9d, 0},
+ {0x9e, 0},
+ {0xa0, 0},
+ {0xa1, 0},
+ {0xa4, 0},
+ {0xa5, 0},
+ {0xa7, 0},
+ {0xa8, 0},
+ {0xac, 0},
+ {0xad, 0},
+ {0xaf, 0},
+ {0xb1, 0},
+ {0xb6, 0},
+ {0xb9, 0},
+ {0xbf, 0},
+ {0xcf, 0},
+ },
+ /* 154 */
+ {
+ {0xc000, 147},
+ {0xc000, 149},
+ {0xc000, 150},
+ {0xc000, 151},
+ {0xc000, 152},
+ {0xc000, 155},
+ {0xc000, 157},
+ {0xc000, 158},
+ {0xc000, 165},
+ {0xc000, 166},
+ {0xc000, 168},
+ {0xc000, 174},
+ {0xc000, 175},
+ {0xc000, 180},
+ {0xc000, 182},
+ {0xc000, 183},
+ },
+ /* 155 */
+ {
+ {0x8001, 147},
+ {0xc016, 147},
+ {0x8001, 149},
+ {0xc016, 149},
+ {0x8001, 150},
+ {0xc016, 150},
+ {0x8001, 151},
+ {0xc016, 151},
+ {0x8001, 152},
+ {0xc016, 152},
+ {0x8001, 155},
+ {0xc016, 155},
+ {0x8001, 157},
+ {0xc016, 157},
+ {0x8001, 158},
+ {0xc016, 158},
+ },
+ /* 156 */
+ {
+ {0x8002, 147},
+ {0x8009, 147},
+ {0x8017, 147},
+ {0xc028, 147},
+ {0x8002, 149},
+ {0x8009, 149},
+ {0x8017, 149},
+ {0xc028, 149},
+ {0x8002, 150},
+ {0x8009, 150},
+ {0x8017, 150},
+ {0xc028, 150},
+ {0x8002, 151},
+ {0x8009, 151},
+ {0x8017, 151},
+ {0xc028, 151},
+ },
+ /* 157 */
+ {
+ {0x8003, 147},
+ {0x8006, 147},
+ {0x800a, 147},
+ {0x800f, 147},
+ {0x8018, 147},
+ {0x801f, 147},
+ {0x8029, 147},
+ {0xc038, 147},
+ {0x8003, 149},
+ {0x8006, 149},
+ {0x800a, 149},
+ {0x800f, 149},
+ {0x8018, 149},
+ {0x801f, 149},
+ {0x8029, 149},
+ {0xc038, 149},
+ },
+ /* 158 */
+ {
+ {0x8003, 150},
+ {0x8006, 150},
+ {0x800a, 150},
+ {0x800f, 150},
+ {0x8018, 150},
+ {0x801f, 150},
+ {0x8029, 150},
+ {0xc038, 150},
+ {0x8003, 151},
+ {0x8006, 151},
+ {0x800a, 151},
+ {0x800f, 151},
+ {0x8018, 151},
+ {0x801f, 151},
+ {0x8029, 151},
+ {0xc038, 151},
+ },
+ /* 159 */
+ {
+ {0x8002, 152},
+ {0x8009, 152},
+ {0x8017, 152},
+ {0xc028, 152},
+ {0x8002, 155},
+ {0x8009, 155},
+ {0x8017, 155},
+ {0xc028, 155},
+ {0x8002, 157},
+ {0x8009, 157},
+ {0x8017, 157},
+ {0xc028, 157},
+ {0x8002, 158},
+ {0x8009, 158},
+ {0x8017, 158},
+ {0xc028, 158},
+ },
+ /* 160 */
+ {
+ {0x8003, 152},
+ {0x8006, 152},
+ {0x800a, 152},
+ {0x800f, 152},
+ {0x8018, 152},
+ {0x801f, 152},
+ {0x8029, 152},
+ {0xc038, 152},
+ {0x8003, 155},
+ {0x8006, 155},
+ {0x800a, 155},
+ {0x800f, 155},
+ {0x8018, 155},
+ {0x801f, 155},
+ {0x8029, 155},
+ {0xc038, 155},
+ },
+ /* 161 */
+ {
+ {0x8003, 157},
+ {0x8006, 157},
+ {0x800a, 157},
+ {0x800f, 157},
+ {0x8018, 157},
+ {0x801f, 157},
+ {0x8029, 157},
+ {0xc038, 157},
+ {0x8003, 158},
+ {0x8006, 158},
+ {0x800a, 158},
+ {0x800f, 158},
+ {0x8018, 158},
+ {0x801f, 158},
+ {0x8029, 158},
+ {0xc038, 158},
+ },
+ /* 162 */
+ {
+ {0x8001, 165},
+ {0xc016, 165},
+ {0x8001, 166},
+ {0xc016, 166},
+ {0x8001, 168},
+ {0xc016, 168},
+ {0x8001, 174},
+ {0xc016, 174},
+ {0x8001, 175},
+ {0xc016, 175},
+ {0x8001, 180},
+ {0xc016, 180},
+ {0x8001, 182},
+ {0xc016, 182},
+ {0x8001, 183},
+ {0xc016, 183},
+ },
+ /* 163 */
+ {
+ {0x8002, 165},
+ {0x8009, 165},
+ {0x8017, 165},
+ {0xc028, 165},
+ {0x8002, 166},
+ {0x8009, 166},
+ {0x8017, 166},
+ {0xc028, 166},
+ {0x8002, 168},
+ {0x8009, 168},
+ {0x8017, 168},
+ {0xc028, 168},
+ {0x8002, 174},
+ {0x8009, 174},
+ {0x8017, 174},
+ {0xc028, 174},
+ },
+ /* 164 */
+ {
+ {0x8003, 165},
+ {0x8006, 165},
+ {0x800a, 165},
+ {0x800f, 165},
+ {0x8018, 165},
+ {0x801f, 165},
+ {0x8029, 165},
+ {0xc038, 165},
+ {0x8003, 166},
+ {0x8006, 166},
+ {0x800a, 166},
+ {0x800f, 166},
+ {0x8018, 166},
+ {0x801f, 166},
+ {0x8029, 166},
+ {0xc038, 166},
+ },
+ /* 165 */
+ {
+ {0x8003, 168},
+ {0x8006, 168},
+ {0x800a, 168},
+ {0x800f, 168},
+ {0x8018, 168},
+ {0x801f, 168},
+ {0x8029, 168},
+ {0xc038, 168},
+ {0x8003, 174},
+ {0x8006, 174},
+ {0x800a, 174},
+ {0x800f, 174},
+ {0x8018, 174},
+ {0x801f, 174},
+ {0x8029, 174},
+ {0xc038, 174},
+ },
+ /* 166 */
+ {
+ {0x8002, 175},
+ {0x8009, 175},
+ {0x8017, 175},
+ {0xc028, 175},
+ {0x8002, 180},
+ {0x8009, 180},
+ {0x8017, 180},
+ {0xc028, 180},
+ {0x8002, 182},
+ {0x8009, 182},
+ {0x8017, 182},
+ {0xc028, 182},
+ {0x8002, 183},
+ {0x8009, 183},
+ {0x8017, 183},
+ {0xc028, 183},
+ },
+ /* 167 */
+ {
+ {0x8003, 175},
+ {0x8006, 175},
+ {0x800a, 175},
+ {0x800f, 175},
+ {0x8018, 175},
+ {0x801f, 175},
+ {0x8029, 175},
+ {0xc038, 175},
+ {0x8003, 180},
+ {0x8006, 180},
+ {0x800a, 180},
+ {0x800f, 180},
+ {0x8018, 180},
+ {0x801f, 180},
+ {0x8029, 180},
+ {0xc038, 180},
+ },
+ /* 168 */
+ {
+ {0x8003, 182},
+ {0x8006, 182},
+ {0x800a, 182},
+ {0x800f, 182},
+ {0x8018, 182},
+ {0x801f, 182},
+ {0x8029, 182},
+ {0xc038, 182},
+ {0x8003, 183},
+ {0x8006, 183},
+ {0x800a, 183},
+ {0x800f, 183},
+ {0x8018, 183},
+ {0x801f, 183},
+ {0x8029, 183},
+ {0xc038, 183},
+ },
+ /* 169 */
+ {
+ {0xc000, 188},
+ {0xc000, 191},
+ {0xc000, 197},
+ {0xc000, 231},
+ {0xc000, 239},
+ {0xb0, 0},
+ {0xb2, 0},
+ {0xb3, 0},
+ {0xb7, 0},
+ {0xb8, 0},
+ {0xba, 0},
+ {0xbb, 0},
+ {0xc0, 0},
+ {0xc7, 0},
+ {0xd0, 0},
+ {0xdf, 0},
+ },
+ /* 170 */
+ {
+ {0x8001, 188},
+ {0xc016, 188},
+ {0x8001, 191},
+ {0xc016, 191},
+ {0x8001, 197},
+ {0xc016, 197},
+ {0x8001, 231},
+ {0xc016, 231},
+ {0x8001, 239},
+ {0xc016, 239},
+ {0xc000, 9},
+ {0xc000, 142},
+ {0xc000, 144},
+ {0xc000, 145},
+ {0xc000, 148},
+ {0xc000, 159},
+ },
+ /* 171 */
+ {
+ {0x8002, 188},
+ {0x8009, 188},
+ {0x8017, 188},
+ {0xc028, 188},
+ {0x8002, 191},
+ {0x8009, 191},
+ {0x8017, 191},
+ {0xc028, 191},
+ {0x8002, 197},
+ {0x8009, 197},
+ {0x8017, 197},
+ {0xc028, 197},
+ {0x8002, 231},
+ {0x8009, 231},
+ {0x8017, 231},
+ {0xc028, 231},
+ },
+ /* 172 */
+ {
+ {0x8003, 188},
+ {0x8006, 188},
+ {0x800a, 188},
+ {0x800f, 188},
+ {0x8018, 188},
+ {0x801f, 188},
+ {0x8029, 188},
+ {0xc038, 188},
+ {0x8003, 191},
+ {0x8006, 191},
+ {0x800a, 191},
+ {0x800f, 191},
+ {0x8018, 191},
+ {0x801f, 191},
+ {0x8029, 191},
+ {0xc038, 191},
+ },
+ /* 173 */
+ {
+ {0x8003, 197},
+ {0x8006, 197},
+ {0x800a, 197},
+ {0x800f, 197},
+ {0x8018, 197},
+ {0x801f, 197},
+ {0x8029, 197},
+ {0xc038, 197},
+ {0x8003, 231},
+ {0x8006, 231},
+ {0x800a, 231},
+ {0x800f, 231},
+ {0x8018, 231},
+ {0x801f, 231},
+ {0x8029, 231},
+ {0xc038, 231},
+ },
+ /* 174 */
+ {
+ {0x8002, 239},
+ {0x8009, 239},
+ {0x8017, 239},
+ {0xc028, 239},
+ {0x8001, 9},
+ {0xc016, 9},
+ {0x8001, 142},
+ {0xc016, 142},
+ {0x8001, 144},
+ {0xc016, 144},
+ {0x8001, 145},
+ {0xc016, 145},
+ {0x8001, 148},
+ {0xc016, 148},
+ {0x8001, 159},
+ {0xc016, 159},
+ },
+ /* 175 */
+ {
+ {0x8003, 239},
+ {0x8006, 239},
+ {0x800a, 239},
+ {0x800f, 239},
+ {0x8018, 239},
+ {0x801f, 239},
+ {0x8029, 239},
+ {0xc038, 239},
+ {0x8002, 9},
+ {0x8009, 9},
+ {0x8017, 9},
+ {0xc028, 9},
+ {0x8002, 142},
+ {0x8009, 142},
+ {0x8017, 142},
+ {0xc028, 142},
+ },
+ /* 176 */
+ {
+ {0x8003, 9},
+ {0x8006, 9},
+ {0x800a, 9},
+ {0x800f, 9},
+ {0x8018, 9},
+ {0x801f, 9},
+ {0x8029, 9},
+ {0xc038, 9},
+ {0x8003, 142},
+ {0x8006, 142},
+ {0x800a, 142},
+ {0x800f, 142},
+ {0x8018, 142},
+ {0x801f, 142},
+ {0x8029, 142},
+ {0xc038, 142},
+ },
+ /* 177 */
+ {
+ {0x8002, 144},
+ {0x8009, 144},
+ {0x8017, 144},
+ {0xc028, 144},
+ {0x8002, 145},
+ {0x8009, 145},
+ {0x8017, 145},
+ {0xc028, 145},
+ {0x8002, 148},
+ {0x8009, 148},
+ {0x8017, 148},
+ {0xc028, 148},
+ {0x8002, 159},
+ {0x8009, 159},
+ {0x8017, 159},
+ {0xc028, 159},
+ },
+ /* 178 */
+ {
+ {0x8003, 144},
+ {0x8006, 144},
+ {0x800a, 144},
+ {0x800f, 144},
+ {0x8018, 144},
+ {0x801f, 144},
+ {0x8029, 144},
+ {0xc038, 144},
+ {0x8003, 145},
+ {0x8006, 145},
+ {0x800a, 145},
+ {0x800f, 145},
+ {0x8018, 145},
+ {0x801f, 145},
+ {0x8029, 145},
+ {0xc038, 145},
+ },
+ /* 179 */
+ {
+ {0x8003, 148},
+ {0x8006, 148},
+ {0x800a, 148},
+ {0x800f, 148},
+ {0x8018, 148},
+ {0x801f, 148},
+ {0x8029, 148},
+ {0xc038, 148},
+ {0x8003, 159},
+ {0x8006, 159},
+ {0x800a, 159},
+ {0x800f, 159},
+ {0x8018, 159},
+ {0x801f, 159},
+ {0x8029, 159},
+ {0xc038, 159},
+ },
+ /* 180 */
+ {
+ {0xc000, 171},
+ {0xc000, 206},
+ {0xc000, 215},
+ {0xc000, 225},
+ {0xc000, 236},
+ {0xc000, 237},
+ {0xbc, 0},
+ {0xbd, 0},
+ {0xc1, 0},
+ {0xc4, 0},
+ {0xc8, 0},
+ {0xcb, 0},
+ {0xd1, 0},
+ {0xd8, 0},
+ {0xe0, 0},
+ {0xee, 0},
+ },
+ /* 181 */
+ {
+ {0x8001, 171},
+ {0xc016, 171},
+ {0x8001, 206},
+ {0xc016, 206},
+ {0x8001, 215},
+ {0xc016, 215},
+ {0x8001, 225},
+ {0xc016, 225},
+ {0x8001, 236},
+ {0xc016, 236},
+ {0x8001, 237},
+ {0xc016, 237},
+ {0xc000, 199},
+ {0xc000, 207},
+ {0xc000, 234},
+ {0xc000, 235},
+ },
+ /* 182 */
+ {
+ {0x8002, 171},
+ {0x8009, 171},
+ {0x8017, 171},
+ {0xc028, 171},
+ {0x8002, 206},
+ {0x8009, 206},
+ {0x8017, 206},
+ {0xc028, 206},
+ {0x8002, 215},
+ {0x8009, 215},
+ {0x8017, 215},
+ {0xc028, 215},
+ {0x8002, 225},
+ {0x8009, 225},
+ {0x8017, 225},
+ {0xc028, 225},
+ },
+ /* 183 */
+ {
+ {0x8003, 171},
+ {0x8006, 171},
+ {0x800a, 171},
+ {0x800f, 171},
+ {0x8018, 171},
+ {0x801f, 171},
+ {0x8029, 171},
+ {0xc038, 171},
+ {0x8003, 206},
+ {0x8006, 206},
+ {0x800a, 206},
+ {0x800f, 206},
+ {0x8018, 206},
+ {0x801f, 206},
+ {0x8029, 206},
+ {0xc038, 206},
+ },
+ /* 184 */
+ {
+ {0x8003, 215},
+ {0x8006, 215},
+ {0x800a, 215},
+ {0x800f, 215},
+ {0x8018, 215},
+ {0x801f, 215},
+ {0x8029, 215},
+ {0xc038, 215},
+ {0x8003, 225},
+ {0x8006, 225},
+ {0x800a, 225},
+ {0x800f, 225},
+ {0x8018, 225},
+ {0x801f, 225},
+ {0x8029, 225},
+ {0xc038, 225},
+ },
+ /* 185 */
+ {
+ {0x8002, 236},
+ {0x8009, 236},
+ {0x8017, 236},
+ {0xc028, 236},
+ {0x8002, 237},
+ {0x8009, 237},
+ {0x8017, 237},
+ {0xc028, 237},
+ {0x8001, 199},
+ {0xc016, 199},
+ {0x8001, 207},
+ {0xc016, 207},
+ {0x8001, 234},
+ {0xc016, 234},
+ {0x8001, 235},
+ {0xc016, 235},
+ },
+ /* 186 */
+ {
+ {0x8003, 236},
+ {0x8006, 236},
+ {0x800a, 236},
+ {0x800f, 236},
+ {0x8018, 236},
+ {0x801f, 236},
+ {0x8029, 236},
+ {0xc038, 236},
+ {0x8003, 237},
+ {0x8006, 237},
+ {0x800a, 237},
+ {0x800f, 237},
+ {0x8018, 237},
+ {0x801f, 237},
+ {0x8029, 237},
+ {0xc038, 237},
+ },
+ /* 187 */
+ {
+ {0x8002, 199},
+ {0x8009, 199},
+ {0x8017, 199},
+ {0xc028, 199},
+ {0x8002, 207},
+ {0x8009, 207},
+ {0x8017, 207},
+ {0xc028, 207},
+ {0x8002, 234},
+ {0x8009, 234},
+ {0x8017, 234},
+ {0xc028, 234},
+ {0x8002, 235},
+ {0x8009, 235},
+ {0x8017, 235},
+ {0xc028, 235},
+ },
+ /* 188 */
+ {
+ {0x8003, 199},
+ {0x8006, 199},
+ {0x800a, 199},
+ {0x800f, 199},
+ {0x8018, 199},
+ {0x801f, 199},
+ {0x8029, 199},
+ {0xc038, 199},
+ {0x8003, 207},
+ {0x8006, 207},
+ {0x800a, 207},
+ {0x800f, 207},
+ {0x8018, 207},
+ {0x801f, 207},
+ {0x8029, 207},
+ {0xc038, 207},
+ },
+ /* 189 */
+ {
+ {0x8003, 234},
+ {0x8006, 234},
+ {0x800a, 234},
+ {0x800f, 234},
+ {0x8018, 234},
+ {0x801f, 234},
+ {0x8029, 234},
+ {0xc038, 234},
+ {0x8003, 235},
+ {0x8006, 235},
+ {0x800a, 235},
+ {0x800f, 235},
+ {0x8018, 235},
+ {0x801f, 235},
+ {0x8029, 235},
+ {0xc038, 235},
+ },
+ /* 190 */
+ {
+ {0xc2, 0},
+ {0xc3, 0},
+ {0xc5, 0},
+ {0xc6, 0},
+ {0xc9, 0},
+ {0xca, 0},
+ {0xcc, 0},
+ {0xcd, 0},
+ {0xd2, 0},
+ {0xd5, 0},
+ {0xd9, 0},
+ {0xdc, 0},
+ {0xe1, 0},
+ {0xe7, 0},
+ {0xef, 0},
+ {0xf6, 0},
+ },
+ /* 191 */
+ {
+ {0xc000, 192},
+ {0xc000, 193},
+ {0xc000, 200},
+ {0xc000, 201},
+ {0xc000, 202},
+ {0xc000, 205},
+ {0xc000, 210},
+ {0xc000, 213},
+ {0xc000, 218},
+ {0xc000, 219},
+ {0xc000, 238},
+ {0xc000, 240},
+ {0xc000, 242},
+ {0xc000, 243},
+ {0xc000, 255},
+ {0xce, 0},
+ },
+ /* 192 */
+ {
+ {0x8001, 192},
+ {0xc016, 192},
+ {0x8001, 193},
+ {0xc016, 193},
+ {0x8001, 200},
+ {0xc016, 200},
+ {0x8001, 201},
+ {0xc016, 201},
+ {0x8001, 202},
+ {0xc016, 202},
+ {0x8001, 205},
+ {0xc016, 205},
+ {0x8001, 210},
+ {0xc016, 210},
+ {0x8001, 213},
+ {0xc016, 213},
+ },
+ /* 193 */
+ {
+ {0x8002, 192},
+ {0x8009, 192},
+ {0x8017, 192},
+ {0xc028, 192},
+ {0x8002, 193},
+ {0x8009, 193},
+ {0x8017, 193},
+ {0xc028, 193},
+ {0x8002, 200},
+ {0x8009, 200},
+ {0x8017, 200},
+ {0xc028, 200},
+ {0x8002, 201},
+ {0x8009, 201},
+ {0x8017, 201},
+ {0xc028, 201},
+ },
+ /* 194 */
+ {
+ {0x8003, 192},
+ {0x8006, 192},
+ {0x800a, 192},
+ {0x800f, 192},
+ {0x8018, 192},
+ {0x801f, 192},
+ {0x8029, 192},
+ {0xc038, 192},
+ {0x8003, 193},
+ {0x8006, 193},
+ {0x800a, 193},
+ {0x800f, 193},
+ {0x8018, 193},
+ {0x801f, 193},
+ {0x8029, 193},
+ {0xc038, 193},
+ },
+ /* 195 */
+ {
+ {0x8003, 200},
+ {0x8006, 200},
+ {0x800a, 200},
+ {0x800f, 200},
+ {0x8018, 200},
+ {0x801f, 200},
+ {0x8029, 200},
+ {0xc038, 200},
+ {0x8003, 201},
+ {0x8006, 201},
+ {0x800a, 201},
+ {0x800f, 201},
+ {0x8018, 201},
+ {0x801f, 201},
+ {0x8029, 201},
+ {0xc038, 201},
+ },
+ /* 196 */
+ {
+ {0x8002, 202},
+ {0x8009, 202},
+ {0x8017, 202},
+ {0xc028, 202},
+ {0x8002, 205},
+ {0x8009, 205},
+ {0x8017, 205},
+ {0xc028, 205},
+ {0x8002, 210},
+ {0x8009, 210},
+ {0x8017, 210},
+ {0xc028, 210},
+ {0x8002, 213},
+ {0x8009, 213},
+ {0x8017, 213},
+ {0xc028, 213},
+ },
+ /* 197 */
+ {
+ {0x8003, 202},
+ {0x8006, 202},
+ {0x800a, 202},
+ {0x800f, 202},
+ {0x8018, 202},
+ {0x801f, 202},
+ {0x8029, 202},
+ {0xc038, 202},
+ {0x8003, 205},
+ {0x8006, 205},
+ {0x800a, 205},
+ {0x800f, 205},
+ {0x8018, 205},
+ {0x801f, 205},
+ {0x8029, 205},
+ {0xc038, 205},
+ },
+ /* 198 */
+ {
+ {0x8003, 210},
+ {0x8006, 210},
+ {0x800a, 210},
+ {0x800f, 210},
+ {0x8018, 210},
+ {0x801f, 210},
+ {0x8029, 210},
+ {0xc038, 210},
+ {0x8003, 213},
+ {0x8006, 213},
+ {0x800a, 213},
+ {0x800f, 213},
+ {0x8018, 213},
+ {0x801f, 213},
+ {0x8029, 213},
+ {0xc038, 213},
+ },
+ /* 199 */
+ {
+ {0x8001, 218},
+ {0xc016, 218},
+ {0x8001, 219},
+ {0xc016, 219},
+ {0x8001, 238},
+ {0xc016, 238},
+ {0x8001, 240},
+ {0xc016, 240},
+ {0x8001, 242},
+ {0xc016, 242},
+ {0x8001, 243},
+ {0xc016, 243},
+ {0x8001, 255},
+ {0xc016, 255},
+ {0xc000, 203},
+ {0xc000, 204},
+ },
+ /* 200 */
+ {
+ {0x8002, 218},
+ {0x8009, 218},
+ {0x8017, 218},
+ {0xc028, 218},
+ {0x8002, 219},
+ {0x8009, 219},
+ {0x8017, 219},
+ {0xc028, 219},
+ {0x8002, 238},
+ {0x8009, 238},
+ {0x8017, 238},
+ {0xc028, 238},
+ {0x8002, 240},
+ {0x8009, 240},
+ {0x8017, 240},
+ {0xc028, 240},
+ },
+ /* 201 */
+ {
+ {0x8003, 218},
+ {0x8006, 218},
+ {0x800a, 218},
+ {0x800f, 218},
+ {0x8018, 218},
+ {0x801f, 218},
+ {0x8029, 218},
+ {0xc038, 218},
+ {0x8003, 219},
+ {0x8006, 219},
+ {0x800a, 219},
+ {0x800f, 219},
+ {0x8018, 219},
+ {0x801f, 219},
+ {0x8029, 219},
+ {0xc038, 219},
+ },
+ /* 202 */
+ {
+ {0x8003, 238},
+ {0x8006, 238},
+ {0x800a, 238},
+ {0x800f, 238},
+ {0x8018, 238},
+ {0x801f, 238},
+ {0x8029, 238},
+ {0xc038, 238},
+ {0x8003, 240},
+ {0x8006, 240},
+ {0x800a, 240},
+ {0x800f, 240},
+ {0x8018, 240},
+ {0x801f, 240},
+ {0x8029, 240},
+ {0xc038, 240},
+ },
+ /* 203 */
+ {
+ {0x8002, 242},
+ {0x8009, 242},
+ {0x8017, 242},
+ {0xc028, 242},
+ {0x8002, 243},
+ {0x8009, 243},
+ {0x8017, 243},
+ {0xc028, 243},
+ {0x8002, 255},
+ {0x8009, 255},
+ {0x8017, 255},
+ {0xc028, 255},
+ {0x8001, 203},
+ {0xc016, 203},
+ {0x8001, 204},
+ {0xc016, 204},
+ },
+ /* 204 */
+ {
+ {0x8003, 242},
+ {0x8006, 242},
+ {0x800a, 242},
+ {0x800f, 242},
+ {0x8018, 242},
+ {0x801f, 242},
+ {0x8029, 242},
+ {0xc038, 242},
+ {0x8003, 243},
+ {0x8006, 243},
+ {0x800a, 243},
+ {0x800f, 243},
+ {0x8018, 243},
+ {0x801f, 243},
+ {0x8029, 243},
+ {0xc038, 243},
+ },
+ /* 205 */
+ {
+ {0x8003, 255},
+ {0x8006, 255},
+ {0x800a, 255},
+ {0x800f, 255},
+ {0x8018, 255},
+ {0x801f, 255},
+ {0x8029, 255},
+ {0xc038, 255},
+ {0x8002, 203},
+ {0x8009, 203},
+ {0x8017, 203},
+ {0xc028, 203},
+ {0x8002, 204},
+ {0x8009, 204},
+ {0x8017, 204},
+ {0xc028, 204},
+ },
+ /* 206 */
+ {
+ {0x8003, 203},
+ {0x8006, 203},
+ {0x800a, 203},
+ {0x800f, 203},
+ {0x8018, 203},
+ {0x801f, 203},
+ {0x8029, 203},
+ {0xc038, 203},
+ {0x8003, 204},
+ {0x8006, 204},
+ {0x800a, 204},
+ {0x800f, 204},
+ {0x8018, 204},
+ {0x801f, 204},
+ {0x8029, 204},
+ {0xc038, 204},
+ },
+ /* 207 */
+ {
+ {0xd3, 0},
+ {0xd4, 0},
+ {0xd6, 0},
+ {0xd7, 0},
+ {0xda, 0},
+ {0xdb, 0},
+ {0xdd, 0},
+ {0xde, 0},
+ {0xe2, 0},
+ {0xe4, 0},
+ {0xe8, 0},
+ {0xeb, 0},
+ {0xf0, 0},
+ {0xf3, 0},
+ {0xf7, 0},
+ {0xfa, 0},
+ },
+ /* 208 */
+ {
+ {0xc000, 211},
+ {0xc000, 212},
+ {0xc000, 214},
+ {0xc000, 221},
+ {0xc000, 222},
+ {0xc000, 223},
+ {0xc000, 241},
+ {0xc000, 244},
+ {0xc000, 245},
+ {0xc000, 246},
+ {0xc000, 247},
+ {0xc000, 248},
+ {0xc000, 250},
+ {0xc000, 251},
+ {0xc000, 252},
+ {0xc000, 253},
+ },
+ /* 209 */
+ {
+ {0x8001, 211},
+ {0xc016, 211},
+ {0x8001, 212},
+ {0xc016, 212},
+ {0x8001, 214},
+ {0xc016, 214},
+ {0x8001, 221},
+ {0xc016, 221},
+ {0x8001, 222},
+ {0xc016, 222},
+ {0x8001, 223},
+ {0xc016, 223},
+ {0x8001, 241},
+ {0xc016, 241},
+ {0x8001, 244},
+ {0xc016, 244},
+ },
+ /* 210 */
+ {
+ {0x8002, 211},
+ {0x8009, 211},
+ {0x8017, 211},
+ {0xc028, 211},
+ {0x8002, 212},
+ {0x8009, 212},
+ {0x8017, 212},
+ {0xc028, 212},
+ {0x8002, 214},
+ {0x8009, 214},
+ {0x8017, 214},
+ {0xc028, 214},
+ {0x8002, 221},
+ {0x8009, 221},
+ {0x8017, 221},
+ {0xc028, 221},
+ },
+ /* 211 */
+ {
+ {0x8003, 211},
+ {0x8006, 211},
+ {0x800a, 211},
+ {0x800f, 211},
+ {0x8018, 211},
+ {0x801f, 211},
+ {0x8029, 211},
+ {0xc038, 211},
+ {0x8003, 212},
+ {0x8006, 212},
+ {0x800a, 212},
+ {0x800f, 212},
+ {0x8018, 212},
+ {0x801f, 212},
+ {0x8029, 212},
+ {0xc038, 212},
+ },
+ /* 212 */
+ {
+ {0x8003, 214},
+ {0x8006, 214},
+ {0x800a, 214},
+ {0x800f, 214},
+ {0x8018, 214},
+ {0x801f, 214},
+ {0x8029, 214},
+ {0xc038, 214},
+ {0x8003, 221},
+ {0x8006, 221},
+ {0x800a, 221},
+ {0x800f, 221},
+ {0x8018, 221},
+ {0x801f, 221},
+ {0x8029, 221},
+ {0xc038, 221},
+ },
+ /* 213 */
+ {
+ {0x8002, 222},
+ {0x8009, 222},
+ {0x8017, 222},
+ {0xc028, 222},
+ {0x8002, 223},
+ {0x8009, 223},
+ {0x8017, 223},
+ {0xc028, 223},
+ {0x8002, 241},
+ {0x8009, 241},
+ {0x8017, 241},
+ {0xc028, 241},
+ {0x8002, 244},
+ {0x8009, 244},
+ {0x8017, 244},
+ {0xc028, 244},
+ },
+ /* 214 */
+ {
+ {0x8003, 222},
+ {0x8006, 222},
+ {0x800a, 222},
+ {0x800f, 222},
+ {0x8018, 222},
+ {0x801f, 222},
+ {0x8029, 222},
+ {0xc038, 222},
+ {0x8003, 223},
+ {0x8006, 223},
+ {0x800a, 223},
+ {0x800f, 223},
+ {0x8018, 223},
+ {0x801f, 223},
+ {0x8029, 223},
+ {0xc038, 223},
+ },
+ /* 215 */
+ {
+ {0x8003, 241},
+ {0x8006, 241},
+ {0x800a, 241},
+ {0x800f, 241},
+ {0x8018, 241},
+ {0x801f, 241},
+ {0x8029, 241},
+ {0xc038, 241},
+ {0x8003, 244},
+ {0x8006, 244},
+ {0x800a, 244},
+ {0x800f, 244},
+ {0x8018, 244},
+ {0x801f, 244},
+ {0x8029, 244},
+ {0xc038, 244},
+ },
+ /* 216 */
+ {
+ {0x8001, 245},
+ {0xc016, 245},
+ {0x8001, 246},
+ {0xc016, 246},
+ {0x8001, 247},
+ {0xc016, 247},
+ {0x8001, 248},
+ {0xc016, 248},
+ {0x8001, 250},
+ {0xc016, 250},
+ {0x8001, 251},
+ {0xc016, 251},
+ {0x8001, 252},
+ {0xc016, 252},
+ {0x8001, 253},
+ {0xc016, 253},
+ },
+ /* 217 */
+ {
+ {0x8002, 245},
+ {0x8009, 245},
+ {0x8017, 245},
+ {0xc028, 245},
+ {0x8002, 246},
+ {0x8009, 246},
+ {0x8017, 246},
+ {0xc028, 246},
+ {0x8002, 247},
+ {0x8009, 247},
+ {0x8017, 247},
+ {0xc028, 247},
+ {0x8002, 248},
+ {0x8009, 248},
+ {0x8017, 248},
+ {0xc028, 248},
+ },
+ /* 218 */
+ {
+ {0x8003, 245},
+ {0x8006, 245},
+ {0x800a, 245},
+ {0x800f, 245},
+ {0x8018, 245},
+ {0x801f, 245},
+ {0x8029, 245},
+ {0xc038, 245},
+ {0x8003, 246},
+ {0x8006, 246},
+ {0x800a, 246},
+ {0x800f, 246},
+ {0x8018, 246},
+ {0x801f, 246},
+ {0x8029, 246},
+ {0xc038, 246},
+ },
+ /* 219 */
+ {
+ {0x8003, 247},
+ {0x8006, 247},
+ {0x800a, 247},
+ {0x800f, 247},
+ {0x8018, 247},
+ {0x801f, 247},
+ {0x8029, 247},
+ {0xc038, 247},
+ {0x8003, 248},
+ {0x8006, 248},
+ {0x800a, 248},
+ {0x800f, 248},
+ {0x8018, 248},
+ {0x801f, 248},
+ {0x8029, 248},
+ {0xc038, 248},
+ },
+ /* 220 */
+ {
+ {0x8002, 250},
+ {0x8009, 250},
+ {0x8017, 250},
+ {0xc028, 250},
+ {0x8002, 251},
+ {0x8009, 251},
+ {0x8017, 251},
+ {0xc028, 251},
+ {0x8002, 252},
+ {0x8009, 252},
+ {0x8017, 252},
+ {0xc028, 252},
+ {0x8002, 253},
+ {0x8009, 253},
+ {0x8017, 253},
+ {0xc028, 253},
+ },
+ /* 221 */
+ {
+ {0x8003, 250},
+ {0x8006, 250},
+ {0x800a, 250},
+ {0x800f, 250},
+ {0x8018, 250},
+ {0x801f, 250},
+ {0x8029, 250},
+ {0xc038, 250},
+ {0x8003, 251},
+ {0x8006, 251},
+ {0x800a, 251},
+ {0x800f, 251},
+ {0x8018, 251},
+ {0x801f, 251},
+ {0x8029, 251},
+ {0xc038, 251},
+ },
+ /* 222 */
+ {
+ {0x8003, 252},
+ {0x8006, 252},
+ {0x800a, 252},
+ {0x800f, 252},
+ {0x8018, 252},
+ {0x801f, 252},
+ {0x8029, 252},
+ {0xc038, 252},
+ {0x8003, 253},
+ {0x8006, 253},
+ {0x800a, 253},
+ {0x800f, 253},
+ {0x8018, 253},
+ {0x801f, 253},
+ {0x8029, 253},
+ {0xc038, 253},
+ },
+ /* 223 */
+ {
+ {0xc000, 254},
+ {0xe3, 0},
+ {0xe5, 0},
+ {0xe6, 0},
+ {0xe9, 0},
+ {0xea, 0},
+ {0xec, 0},
+ {0xed, 0},
+ {0xf1, 0},
+ {0xf2, 0},
+ {0xf4, 0},
+ {0xf5, 0},
+ {0xf8, 0},
+ {0xf9, 0},
+ {0xfb, 0},
+ {0xfc, 0},
+ },
+ /* 224 */
+ {
+ {0x8001, 254},
+ {0xc016, 254},
+ {0xc000, 2},
+ {0xc000, 3},
+ {0xc000, 4},
+ {0xc000, 5},
+ {0xc000, 6},
+ {0xc000, 7},
+ {0xc000, 8},
+ {0xc000, 11},
+ {0xc000, 12},
+ {0xc000, 14},
+ {0xc000, 15},
+ {0xc000, 16},
+ {0xc000, 17},
+ {0xc000, 18},
+ },
+ /* 225 */
+ {
+ {0x8002, 254},
+ {0x8009, 254},
+ {0x8017, 254},
+ {0xc028, 254},
+ {0x8001, 2},
+ {0xc016, 2},
+ {0x8001, 3},
+ {0xc016, 3},
+ {0x8001, 4},
+ {0xc016, 4},
+ {0x8001, 5},
+ {0xc016, 5},
+ {0x8001, 6},
+ {0xc016, 6},
+ {0x8001, 7},
+ {0xc016, 7},
+ },
+ /* 226 */
+ {
+ {0x8003, 254},
+ {0x8006, 254},
+ {0x800a, 254},
+ {0x800f, 254},
+ {0x8018, 254},
+ {0x801f, 254},
+ {0x8029, 254},
+ {0xc038, 254},
+ {0x8002, 2},
+ {0x8009, 2},
+ {0x8017, 2},
+ {0xc028, 2},
+ {0x8002, 3},
+ {0x8009, 3},
+ {0x8017, 3},
+ {0xc028, 3},
+ },
+ /* 227 */
+ {
+ {0x8003, 2},
+ {0x8006, 2},
+ {0x800a, 2},
+ {0x800f, 2},
+ {0x8018, 2},
+ {0x801f, 2},
+ {0x8029, 2},
+ {0xc038, 2},
+ {0x8003, 3},
+ {0x8006, 3},
+ {0x800a, 3},
+ {0x800f, 3},
+ {0x8018, 3},
+ {0x801f, 3},
+ {0x8029, 3},
+ {0xc038, 3},
+ },
+ /* 228 */
+ {
+ {0x8002, 4},
+ {0x8009, 4},
+ {0x8017, 4},
+ {0xc028, 4},
+ {0x8002, 5},
+ {0x8009, 5},
+ {0x8017, 5},
+ {0xc028, 5},
+ {0x8002, 6},
+ {0x8009, 6},
+ {0x8017, 6},
+ {0xc028, 6},
+ {0x8002, 7},
+ {0x8009, 7},
+ {0x8017, 7},
+ {0xc028, 7},
+ },
+ /* 229 */
+ {
+ {0x8003, 4},
+ {0x8006, 4},
+ {0x800a, 4},
+ {0x800f, 4},
+ {0x8018, 4},
+ {0x801f, 4},
+ {0x8029, 4},
+ {0xc038, 4},
+ {0x8003, 5},
+ {0x8006, 5},
+ {0x800a, 5},
+ {0x800f, 5},
+ {0x8018, 5},
+ {0x801f, 5},
+ {0x8029, 5},
+ {0xc038, 5},
+ },
+ /* 230 */
+ {
+ {0x8003, 6},
+ {0x8006, 6},
+ {0x800a, 6},
+ {0x800f, 6},
+ {0x8018, 6},
+ {0x801f, 6},
+ {0x8029, 6},
+ {0xc038, 6},
+ {0x8003, 7},
+ {0x8006, 7},
+ {0x800a, 7},
+ {0x800f, 7},
+ {0x8018, 7},
+ {0x801f, 7},
+ {0x8029, 7},
+ {0xc038, 7},
+ },
+ /* 231 */
+ {
+ {0x8001, 8},
+ {0xc016, 8},
+ {0x8001, 11},
+ {0xc016, 11},
+ {0x8001, 12},
+ {0xc016, 12},
+ {0x8001, 14},
+ {0xc016, 14},
+ {0x8001, 15},
+ {0xc016, 15},
+ {0x8001, 16},
+ {0xc016, 16},
+ {0x8001, 17},
+ {0xc016, 17},
+ {0x8001, 18},
+ {0xc016, 18},
+ },
+ /* 232 */
+ {
+ {0x8002, 8},
+ {0x8009, 8},
+ {0x8017, 8},
+ {0xc028, 8},
+ {0x8002, 11},
+ {0x8009, 11},
+ {0x8017, 11},
+ {0xc028, 11},
+ {0x8002, 12},
+ {0x8009, 12},
+ {0x8017, 12},
+ {0xc028, 12},
+ {0x8002, 14},
+ {0x8009, 14},
+ {0x8017, 14},
+ {0xc028, 14},
+ },
+ /* 233 */
+ {
+ {0x8003, 8},
+ {0x8006, 8},
+ {0x800a, 8},
+ {0x800f, 8},
+ {0x8018, 8},
+ {0x801f, 8},
+ {0x8029, 8},
+ {0xc038, 8},
+ {0x8003, 11},
+ {0x8006, 11},
+ {0x800a, 11},
+ {0x800f, 11},
+ {0x8018, 11},
+ {0x801f, 11},
+ {0x8029, 11},
+ {0xc038, 11},
+ },
+ /* 234 */
+ {
+ {0x8003, 12},
+ {0x8006, 12},
+ {0x800a, 12},
+ {0x800f, 12},
+ {0x8018, 12},
+ {0x801f, 12},
+ {0x8029, 12},
+ {0xc038, 12},
+ {0x8003, 14},
+ {0x8006, 14},
+ {0x800a, 14},
+ {0x800f, 14},
+ {0x8018, 14},
+ {0x801f, 14},
+ {0x8029, 14},
+ {0xc038, 14},
+ },
+ /* 235 */
+ {
+ {0x8002, 15},
+ {0x8009, 15},
+ {0x8017, 15},
+ {0xc028, 15},
+ {0x8002, 16},
+ {0x8009, 16},
+ {0x8017, 16},
+ {0xc028, 16},
+ {0x8002, 17},
+ {0x8009, 17},
+ {0x8017, 17},
+ {0xc028, 17},
+ {0x8002, 18},
+ {0x8009, 18},
+ {0x8017, 18},
+ {0xc028, 18},
+ },
+ /* 236 */
+ {
+ {0x8003, 15},
+ {0x8006, 15},
+ {0x800a, 15},
+ {0x800f, 15},
+ {0x8018, 15},
+ {0x801f, 15},
+ {0x8029, 15},
+ {0xc038, 15},
+ {0x8003, 16},
+ {0x8006, 16},
+ {0x800a, 16},
+ {0x800f, 16},
+ {0x8018, 16},
+ {0x801f, 16},
+ {0x8029, 16},
+ {0xc038, 16},
+ },
+ /* 237 */
+ {
+ {0x8003, 17},
+ {0x8006, 17},
+ {0x800a, 17},
+ {0x800f, 17},
+ {0x8018, 17},
+ {0x801f, 17},
+ {0x8029, 17},
+ {0xc038, 17},
+ {0x8003, 18},
+ {0x8006, 18},
+ {0x800a, 18},
+ {0x800f, 18},
+ {0x8018, 18},
+ {0x801f, 18},
+ {0x8029, 18},
+ {0xc038, 18},
+ },
+ /* 238 */
+ {
+ {0xc000, 19},
+ {0xc000, 20},
+ {0xc000, 21},
+ {0xc000, 23},
+ {0xc000, 24},
+ {0xc000, 25},
+ {0xc000, 26},
+ {0xc000, 27},
+ {0xc000, 28},
+ {0xc000, 29},
+ {0xc000, 30},
+ {0xc000, 31},
+ {0xc000, 127},
+ {0xc000, 220},
+ {0xc000, 249},
+ {0xfd, 0},
+ },
+ /* 239 */
+ {
+ {0x8001, 19},
+ {0xc016, 19},
+ {0x8001, 20},
+ {0xc016, 20},
+ {0x8001, 21},
+ {0xc016, 21},
+ {0x8001, 23},
+ {0xc016, 23},
+ {0x8001, 24},
+ {0xc016, 24},
+ {0x8001, 25},
+ {0xc016, 25},
+ {0x8001, 26},
+ {0xc016, 26},
+ {0x8001, 27},
+ {0xc016, 27},
+ },
+ /* 240 */
+ {
+ {0x8002, 19},
+ {0x8009, 19},
+ {0x8017, 19},
+ {0xc028, 19},
+ {0x8002, 20},
+ {0x8009, 20},
+ {0x8017, 20},
+ {0xc028, 20},
+ {0x8002, 21},
+ {0x8009, 21},
+ {0x8017, 21},
+ {0xc028, 21},
+ {0x8002, 23},
+ {0x8009, 23},
+ {0x8017, 23},
+ {0xc028, 23},
+ },
+ /* 241 */
+ {
+ {0x8003, 19},
+ {0x8006, 19},
+ {0x800a, 19},
+ {0x800f, 19},
+ {0x8018, 19},
+ {0x801f, 19},
+ {0x8029, 19},
+ {0xc038, 19},
+ {0x8003, 20},
+ {0x8006, 20},
+ {0x800a, 20},
+ {0x800f, 20},
+ {0x8018, 20},
+ {0x801f, 20},
+ {0x8029, 20},
+ {0xc038, 20},
+ },
+ /* 242 */
+ {
+ {0x8003, 21},
+ {0x8006, 21},
+ {0x800a, 21},
+ {0x800f, 21},
+ {0x8018, 21},
+ {0x801f, 21},
+ {0x8029, 21},
+ {0xc038, 21},
+ {0x8003, 23},
+ {0x8006, 23},
+ {0x800a, 23},
+ {0x800f, 23},
+ {0x8018, 23},
+ {0x801f, 23},
+ {0x8029, 23},
+ {0xc038, 23},
+ },
+ /* 243 */
+ {
+ {0x8002, 24},
+ {0x8009, 24},
+ {0x8017, 24},
+ {0xc028, 24},
+ {0x8002, 25},
+ {0x8009, 25},
+ {0x8017, 25},
+ {0xc028, 25},
+ {0x8002, 26},
+ {0x8009, 26},
+ {0x8017, 26},
+ {0xc028, 26},
+ {0x8002, 27},
+ {0x8009, 27},
+ {0x8017, 27},
+ {0xc028, 27},
+ },
+ /* 244 */
+ {
+ {0x8003, 24},
+ {0x8006, 24},
+ {0x800a, 24},
+ {0x800f, 24},
+ {0x8018, 24},
+ {0x801f, 24},
+ {0x8029, 24},
+ {0xc038, 24},
+ {0x8003, 25},
+ {0x8006, 25},
+ {0x800a, 25},
+ {0x800f, 25},
+ {0x8018, 25},
+ {0x801f, 25},
+ {0x8029, 25},
+ {0xc038, 25},
+ },
+ /* 245 */
+ {
+ {0x8003, 26},
+ {0x8006, 26},
+ {0x800a, 26},
+ {0x800f, 26},
+ {0x8018, 26},
+ {0x801f, 26},
+ {0x8029, 26},
+ {0xc038, 26},
+ {0x8003, 27},
+ {0x8006, 27},
+ {0x800a, 27},
+ {0x800f, 27},
+ {0x8018, 27},
+ {0x801f, 27},
+ {0x8029, 27},
+ {0xc038, 27},
+ },
+ /* 246 */
+ {
+ {0x8001, 28},
+ {0xc016, 28},
+ {0x8001, 29},
+ {0xc016, 29},
+ {0x8001, 30},
+ {0xc016, 30},
+ {0x8001, 31},
+ {0xc016, 31},
+ {0x8001, 127},
+ {0xc016, 127},
+ {0x8001, 220},
+ {0xc016, 220},
+ {0x8001, 249},
+ {0xc016, 249},
+ {0xfe, 0},
+ {0xff, 0},
+ },
+ /* 247 */
+ {
+ {0x8002, 28},
+ {0x8009, 28},
+ {0x8017, 28},
+ {0xc028, 28},
+ {0x8002, 29},
+ {0x8009, 29},
+ {0x8017, 29},
+ {0xc028, 29},
+ {0x8002, 30},
+ {0x8009, 30},
+ {0x8017, 30},
+ {0xc028, 30},
+ {0x8002, 31},
+ {0x8009, 31},
+ {0x8017, 31},
+ {0xc028, 31},
+ },
+ /* 248 */
+ {
+ {0x8003, 28},
+ {0x8006, 28},
+ {0x800a, 28},
+ {0x800f, 28},
+ {0x8018, 28},
+ {0x801f, 28},
+ {0x8029, 28},
+ {0xc038, 28},
+ {0x8003, 29},
+ {0x8006, 29},
+ {0x800a, 29},
+ {0x800f, 29},
+ {0x8018, 29},
+ {0x801f, 29},
+ {0x8029, 29},
+ {0xc038, 29},
+ },
+ /* 249 */
+ {
+ {0x8003, 30},
+ {0x8006, 30},
+ {0x800a, 30},
+ {0x800f, 30},
+ {0x8018, 30},
+ {0x801f, 30},
+ {0x8029, 30},
+ {0xc038, 30},
+ {0x8003, 31},
+ {0x8006, 31},
+ {0x800a, 31},
+ {0x800f, 31},
+ {0x8018, 31},
+ {0x801f, 31},
+ {0x8029, 31},
+ {0xc038, 31},
+ },
+ /* 250 */
+ {
+ {0x8002, 127},
+ {0x8009, 127},
+ {0x8017, 127},
+ {0xc028, 127},
+ {0x8002, 220},
+ {0x8009, 220},
+ {0x8017, 220},
+ {0xc028, 220},
+ {0x8002, 249},
+ {0x8009, 249},
+ {0x8017, 249},
+ {0xc028, 249},
+ {0xc000, 10},
+ {0xc000, 13},
+ {0xc000, 22},
+ {0x100, 0},
+ },
+ /* 251 */
+ {
+ {0x8003, 127},
+ {0x8006, 127},
+ {0x800a, 127},
+ {0x800f, 127},
+ {0x8018, 127},
+ {0x801f, 127},
+ {0x8029, 127},
+ {0xc038, 127},
+ {0x8003, 220},
+ {0x8006, 220},
+ {0x800a, 220},
+ {0x800f, 220},
+ {0x8018, 220},
+ {0x801f, 220},
+ {0x8029, 220},
+ {0xc038, 220},
+ },
+ /* 252 */
+ {
+ {0x8003, 249},
+ {0x8006, 249},
+ {0x800a, 249},
+ {0x800f, 249},
+ {0x8018, 249},
+ {0x801f, 249},
+ {0x8029, 249},
+ {0xc038, 249},
+ {0x8001, 10},
+ {0xc016, 10},
+ {0x8001, 13},
+ {0xc016, 13},
+ {0x8001, 22},
+ {0xc016, 22},
+ {0x100, 0},
+ {0x100, 0},
+ },
+ /* 253 */
+ {
+ {0x8002, 10},
+ {0x8009, 10},
+ {0x8017, 10},
+ {0xc028, 10},
+ {0x8002, 13},
+ {0x8009, 13},
+ {0x8017, 13},
+ {0xc028, 13},
+ {0x8002, 22},
+ {0x8009, 22},
+ {0x8017, 22},
+ {0xc028, 22},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ },
+ /* 254 */
+ {
+ {0x8003, 10},
+ {0x8006, 10},
+ {0x800a, 10},
+ {0x800f, 10},
+ {0x8018, 10},
+ {0x801f, 10},
+ {0x8029, 10},
+ {0xc038, 10},
+ {0x8003, 13},
+ {0x8006, 13},
+ {0x800a, 13},
+ {0x800f, 13},
+ {0x8018, 13},
+ {0x801f, 13},
+ {0x8029, 13},
+ {0xc038, 13},
+ },
+ /* 255 */
+ {
+ {0x8003, 22},
+ {0x8006, 22},
+ {0x800a, 22},
+ {0x800f, 22},
+ {0x8018, 22},
+ {0x801f, 22},
+ {0x8029, 22},
+ {0xc038, 22},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ },
+ /* 256 */
+ {
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ },
+};
diff --git a/lib/nghttp3_range.c b/lib/nghttp3_range.c
new file mode 100644
index 0000000..0ce7148
--- /dev/null
+++ b/lib/nghttp3_range.c
@@ -0,0 +1,62 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_range.h"
+#include "nghttp3_macro.h"
+
+void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end) {
+ r->begin = begin;
+ r->end = end;
+}
+
+nghttp3_range nghttp3_range_intersect(const nghttp3_range *a,
+ const nghttp3_range *b) {
+ nghttp3_range r = {0, 0};
+ uint64_t begin = nghttp3_max(a->begin, b->begin);
+ uint64_t end = nghttp3_min(a->end, b->end);
+ if (begin < end) {
+ nghttp3_range_init(&r, begin, end);
+ }
+ return r;
+}
+
+uint64_t nghttp3_range_len(const nghttp3_range *r) { return r->end - r->begin; }
+
+int nghttp3_range_eq(const nghttp3_range *a, const nghttp3_range *b) {
+ return a->begin == b->begin && a->end == b->end;
+}
+
+void nghttp3_range_cut(nghttp3_range *left, nghttp3_range *right,
+ const nghttp3_range *a, const nghttp3_range *b) {
+ /* Assume that b is included in a */
+ left->begin = a->begin;
+ left->end = b->begin;
+ right->begin = b->end;
+ right->end = a->end;
+}
+
+int nghttp3_range_not_after(const nghttp3_range *a, const nghttp3_range *b) {
+ return a->end <= b->end;
+}
diff --git a/lib/nghttp3_range.h b/lib/nghttp3_range.h
new file mode 100644
index 0000000..20dab69
--- /dev/null
+++ b/lib/nghttp3_range.h
@@ -0,0 +1,81 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_RANGE_H
+#define NGHTTP3_RANGE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+/*
+ * nghttp3_range represents half-closed range [begin, end).
+ */
+typedef struct nghttp3_range {
+ uint64_t begin;
+ uint64_t end;
+} nghttp3_range;
+
+/*
+ * nghttp3_range_init initializes |r| with the range [|begin|, |end|).
+ */
+void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end);
+
+/*
+ * nghttp3_range_intersect returns the intersection of |a| and |b|.
+ * If they do not overlap, it returns empty range.
+ */
+nghttp3_range nghttp3_range_intersect(const nghttp3_range *a,
+ const nghttp3_range *b);
+
+/*
+ * nghttp3_range_len returns the length of |r|.
+ */
+uint64_t nghttp3_range_len(const nghttp3_range *r);
+
+/*
+ * nghttp3_range_eq returns nonzero if |a| equals |b|, such that
+ * a->begin == b->begin, and a->end == b->end hold.
+ */
+int nghttp3_range_eq(const nghttp3_range *a, const nghttp3_range *b);
+
+/*
+ * nghttp3_range_cut returns the left and right range after removing
+ * |b| from |a|. This function assumes that |a| completely includes
+ * |b|. In other words, a->begin <= b->begin and b->end <= a->end
+ * hold.
+ */
+void nghttp3_range_cut(nghttp3_range *left, nghttp3_range *right,
+ const nghttp3_range *a, const nghttp3_range *b);
+
+/*
+ * nghttp3_range_not_after returns nonzero if the right edge of |a|
+ * does not go beyond of the right edge of |b|.
+ */
+int nghttp3_range_not_after(const nghttp3_range *a, const nghttp3_range *b);
+
+#endif /* NGHTTP3_RANGE_H */
diff --git a/lib/nghttp3_rcbuf.c b/lib/nghttp3_rcbuf.c
new file mode 100644
index 0000000..9e9dab5
--- /dev/null
+++ b/lib/nghttp3_rcbuf.c
@@ -0,0 +1,108 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_rcbuf.h"
+
+#include <assert.h>
+
+#include "nghttp3_mem.h"
+#include "nghttp3_str.h"
+
+int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size,
+ const nghttp3_mem *mem) {
+ uint8_t *p;
+
+ p = nghttp3_mem_malloc(mem, sizeof(nghttp3_rcbuf) + size);
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ *rcbuf_ptr = (void *)p;
+
+ (*rcbuf_ptr)->mem = mem;
+ (*rcbuf_ptr)->base = p + sizeof(nghttp3_rcbuf);
+ (*rcbuf_ptr)->len = size;
+ (*rcbuf_ptr)->ref = 1;
+
+ return 0;
+}
+
+int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src,
+ size_t srclen, const nghttp3_mem *mem) {
+ int rv;
+ uint8_t *p;
+
+ rv = nghttp3_rcbuf_new(rcbuf_ptr, srclen + 1, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ (*rcbuf_ptr)->len = srclen;
+ p = (*rcbuf_ptr)->base;
+
+ if (srclen) {
+ p = nghttp3_cpymem(p, src, srclen);
+ }
+
+ *p = '\0';
+
+ return 0;
+}
+
+/*
+ * Frees |rcbuf| itself, regardless of its reference cout.
+ */
+void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf) {
+ nghttp3_mem_free(rcbuf->mem, rcbuf);
+}
+
+void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf) {
+ if (rcbuf->ref == -1) {
+ return;
+ }
+
+ ++rcbuf->ref;
+}
+
+void nghttp3_rcbuf_decref(nghttp3_rcbuf *rcbuf) {
+ if (rcbuf == NULL || rcbuf->ref == -1) {
+ return;
+ }
+
+ assert(rcbuf->ref > 0);
+
+ if (--rcbuf->ref == 0) {
+ nghttp3_rcbuf_del(rcbuf);
+ }
+}
+
+nghttp3_vec nghttp3_rcbuf_get_buf(const nghttp3_rcbuf *rcbuf) {
+ nghttp3_vec res = {rcbuf->base, rcbuf->len};
+ return res;
+}
+
+int nghttp3_rcbuf_is_static(const nghttp3_rcbuf *rcbuf) {
+ return rcbuf->ref == -1;
+}
diff --git a/lib/nghttp3_rcbuf.h b/lib/nghttp3_rcbuf.h
new file mode 100644
index 0000000..f589c37
--- /dev/null
+++ b/lib/nghttp3_rcbuf.h
@@ -0,0 +1,81 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_RCBUF_H
+#define NGHTTP3_RCBUF_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+struct nghttp3_rcbuf {
+ /* mem is the memory allocator that allocates memory for this
+ object. */
+ const nghttp3_mem *mem;
+ /* The pointer to the underlying buffer */
+ uint8_t *base;
+ /* Size of buffer pointed by |base|. */
+ size_t len;
+ /* Reference count */
+ int32_t ref;
+};
+
+/*
+ * Allocates nghttp3_rcbuf object with |size| as initial buffer size.
+ * When the function succeeds, the reference count becomes 1.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM:
+ * Out of memory.
+ */
+int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size,
+ const nghttp3_mem *mem);
+
+/*
+ * Like nghttp3_rcbuf_new(), but initializes the buffer with |src| of
+ * length |srclen|. This function allocates additional byte at the
+ * end and puts '\0' into it, so that the resulting buffer could be
+ * used as NULL-terminated string. Still (*rcbuf_ptr)->len equals to
+ * |srclen|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM:
+ * Out of memory.
+ */
+int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src,
+ size_t srclen, const nghttp3_mem *mem);
+
+/*
+ * Frees |rcbuf| itself, regardless of its reference cout.
+ */
+void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf);
+
+#endif /* NGHTTP3_RCBUF_H */
diff --git a/lib/nghttp3_ringbuf.c b/lib/nghttp3_ringbuf.c
new file mode 100644
index 0000000..75ef7ad
--- /dev/null
+++ b/lib/nghttp3_ringbuf.c
@@ -0,0 +1,159 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_ringbuf.h"
+
+#include <assert.h>
+#include <string.h>
+#ifdef WIN32
+# include <intrin.h>
+#endif
+
+#include "nghttp3_macro.h"
+
+#if defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
+unsigned int __popcnt(unsigned int x) {
+ unsigned int c = 0;
+ for (; x; ++c) {
+ x &= x - 1;
+ }
+ return c;
+}
+#endif
+
+int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size,
+ const nghttp3_mem *mem) {
+ if (nmemb) {
+#ifdef WIN32
+ assert(1 == __popcnt((unsigned int)nmemb));
+#else
+ assert(1 == __builtin_popcount((unsigned int)nmemb));
+#endif
+
+ rb->buf = nghttp3_mem_malloc(mem, nmemb * size);
+ if (rb->buf == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+ } else {
+ rb->buf = NULL;
+ }
+
+ rb->mem = mem;
+ rb->nmemb = nmemb;
+ rb->size = size;
+ rb->first = 0;
+ rb->len = 0;
+
+ return 0;
+}
+
+void nghttp3_ringbuf_free(nghttp3_ringbuf *rb) {
+ if (rb == NULL) {
+ return;
+ }
+
+ nghttp3_mem_free(rb->mem, rb->buf);
+}
+
+void *nghttp3_ringbuf_push_front(nghttp3_ringbuf *rb) {
+ rb->first = (rb->first - 1) & (rb->nmemb - 1);
+ rb->len = nghttp3_min(rb->nmemb, rb->len + 1);
+
+ return (void *)&rb->buf[rb->first * rb->size];
+}
+
+void *nghttp3_ringbuf_push_back(nghttp3_ringbuf *rb) {
+ size_t offset = (rb->first + rb->len) & (rb->nmemb - 1);
+
+ if (rb->len == rb->nmemb) {
+ rb->first = (rb->first + 1) & (rb->nmemb - 1);
+ } else {
+ ++rb->len;
+ }
+
+ return (void *)&rb->buf[offset * rb->size];
+}
+
+void nghttp3_ringbuf_pop_front(nghttp3_ringbuf *rb) {
+ rb->first = (rb->first + 1) & (rb->nmemb - 1);
+ --rb->len;
+}
+
+void nghttp3_ringbuf_pop_back(nghttp3_ringbuf *rb) {
+ assert(rb->len);
+ --rb->len;
+}
+
+void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len) {
+ assert(len <= rb->nmemb);
+ rb->len = len;
+}
+
+void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset) {
+ assert(offset < rb->len);
+ offset = (rb->first + offset) & (rb->nmemb - 1);
+ return &rb->buf[offset * rb->size];
+}
+
+int nghttp3_ringbuf_full(nghttp3_ringbuf *rb) { return rb->len == rb->nmemb; }
+
+int nghttp3_ringbuf_reserve(nghttp3_ringbuf *rb, size_t nmemb) {
+ uint8_t *buf;
+
+ if (rb->nmemb >= nmemb) {
+ return 0;
+ }
+
+#ifdef WIN32
+ assert(1 == __popcnt((unsigned int)nmemb));
+#else
+ assert(1 == __builtin_popcount((unsigned int)nmemb));
+#endif
+
+ buf = nghttp3_mem_malloc(rb->mem, nmemb * rb->size);
+ if (buf == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ if (rb->buf != NULL) {
+ if (rb->first + rb->len <= rb->nmemb) {
+ memcpy(buf, rb->buf + rb->first * rb->size, rb->len * rb->size);
+ rb->first = 0;
+ } else {
+ memcpy(buf, rb->buf + rb->first * rb->size,
+ (rb->nmemb - rb->first) * rb->size);
+ memcpy(buf + (rb->nmemb - rb->first) * rb->size, rb->buf,
+ (rb->len - (rb->nmemb - rb->first)) * rb->size);
+ rb->first = 0;
+ }
+
+ nghttp3_mem_free(rb->mem, rb->buf);
+ }
+
+ rb->buf = buf;
+ rb->nmemb = nmemb;
+
+ return 0;
+}
diff --git a/lib/nghttp3_ringbuf.h b/lib/nghttp3_ringbuf.h
new file mode 100644
index 0000000..8e05ec5
--- /dev/null
+++ b/lib/nghttp3_ringbuf.h
@@ -0,0 +1,113 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_RINGBUF_H
+#define NGHTTP3_RINGBUF_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+
+typedef struct nghttp3_ringbuf {
+ /* buf points to the underlying buffer. */
+ uint8_t *buf;
+ const nghttp3_mem *mem;
+ /* nmemb is the number of elements that can be stored in this ring
+ buffer. */
+ size_t nmemb;
+ /* size is the size of each element. */
+ size_t size;
+ /* first is the offset to the first element. */
+ size_t first;
+ /* len is the number of elements actually stored. */
+ size_t len;
+} nghttp3_ringbuf;
+
+/*
+ * nghttp3_ringbuf_init initializes |rb|. |nmemb| is the number of
+ * elements that can be stored in this buffer. |size| is the size of
+ * each element. |size| must be power of 2.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_ringbuf_free frees resources allocated for |rb|. This
+ * function does not free the memory pointed by |rb|.
+ */
+void nghttp3_ringbuf_free(nghttp3_ringbuf *rb);
+
+/* nghttp3_ringbuf_push_front moves the offset to the first element in
+ the buffer backward, and returns the pointer to the element.
+ Caller can store data to the buffer pointed by the returned
+ pointer. If this action exceeds the capacity of the ring buffer,
+ the last element is silently overwritten, and rb->len remains
+ unchanged. */
+void *nghttp3_ringbuf_push_front(nghttp3_ringbuf *rb);
+
+/* nghttp3_ringbuf_push_back moves the offset to the last element in
+ the buffer forward, and returns the pointer to the element. Caller
+ can store data to the buffer pointed by the returned pointer. If
+ this action exceeds the capacity of the ring buffer, the first
+ element is silently overwritten, and rb->len remains unchanged. */
+void *nghttp3_ringbuf_push_back(nghttp3_ringbuf *rb);
+
+/*
+ * nghttp3_ringbuf_pop_front removes first element in |rb|.
+ */
+void nghttp3_ringbuf_pop_front(nghttp3_ringbuf *rb);
+
+/*
+ * nghttp3_ringbuf_pop_back removes the last element in |rb|.
+ */
+void nghttp3_ringbuf_pop_back(nghttp3_ringbuf *rb);
+
+/* nghttp3_ringbuf_resize changes the number of elements stored. This
+ does not change the capacity of the underlying buffer. */
+void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len);
+
+/* nghttp3_ringbuf_get returns the pointer to the element at
+ |offset|. */
+void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset);
+
+/* nghttp3_ringbuf_len returns the number of elements stored. */
+#define nghttp3_ringbuf_len(RB) ((RB)->len)
+
+/* nghttp3_ringbuf_full returns nonzero if |rb| is full. */
+int nghttp3_ringbuf_full(nghttp3_ringbuf *rb);
+
+int nghttp3_ringbuf_reserve(nghttp3_ringbuf *rb, size_t nmemb);
+
+#endif /* NGHTTP3_RINGBUF_H */
diff --git a/lib/nghttp3_str.c b/lib/nghttp3_str.c
new file mode 100644
index 0000000..3782aa7
--- /dev/null
+++ b/lib/nghttp3_str.c
@@ -0,0 +1,110 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_str.h"
+
+#include <string.h>
+#include <assert.h>
+
+uint8_t *nghttp3_cpymem(uint8_t *dest, const uint8_t *src, size_t n) {
+ memcpy(dest, src, n);
+ return dest + n;
+}
+
+/* Generated by gendowncasetbl.py */
+static const uint8_t DOWNCASE_TBL[] = {
+ 0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */,
+ 4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */,
+ 8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */,
+ 12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */,
+ 16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */,
+ 20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */,
+ 24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */,
+ 28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */,
+ 32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */,
+ 36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */,
+ 40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */,
+ 44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */,
+ 48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */,
+ 52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */,
+ 56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */,
+ 60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */,
+ 64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */,
+ 100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */,
+ 104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */,
+ 108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */,
+ 112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */,
+ 116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */,
+ 120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */,
+ 92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */,
+ 96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */,
+ 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */,
+ 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */,
+ 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */,
+ 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */,
+ 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */,
+ 120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */,
+ 124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */,
+ 128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */,
+ 132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */,
+ 136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */,
+ 140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */,
+ 144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */,
+ 148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */,
+ 152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */,
+ 156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */,
+ 160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */,
+ 164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */,
+ 168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */,
+ 172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */,
+ 176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */,
+ 180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */,
+ 184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */,
+ 188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */,
+ 192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */,
+ 196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */,
+ 200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */,
+ 204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */,
+ 208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */,
+ 212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */,
+ 216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */,
+ 220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */,
+ 224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */,
+ 228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */,
+ 232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */,
+ 236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */,
+ 240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */,
+ 244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */,
+ 248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */,
+ 252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */,
+};
+
+void nghttp3_downcase(uint8_t *s, size_t len) {
+ size_t i;
+ for (i = 0; i < len; ++i) {
+ s[i] = DOWNCASE_TBL[s[i]];
+ }
+}
diff --git a/lib/nghttp3_str.h b/lib/nghttp3_str.h
new file mode 100644
index 0000000..19c1d2c
--- /dev/null
+++ b/lib/nghttp3_str.h
@@ -0,0 +1,40 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_STR_H
+#define NGHTTP3_STR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+uint8_t *nghttp3_cpymem(uint8_t *dest, const uint8_t *src, size_t n);
+
+void nghttp3_downcase(uint8_t *s, size_t len);
+
+#endif /* NGHTTP3_STR_H */
diff --git a/lib/nghttp3_stream.c b/lib/nghttp3_stream.c
new file mode 100644
index 0000000..457f5e4
--- /dev/null
+++ b/lib/nghttp3_stream.c
@@ -0,0 +1,1248 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_stream.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "nghttp3_conv.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_frame.h"
+#include "nghttp3_conn.h"
+#include "nghttp3_str.h"
+#include "nghttp3_http.h"
+#include "nghttp3_vec.h"
+#include "nghttp3_unreachable.h"
+
+/* NGHTTP3_STREAM_MAX_COPY_THRES is the maximum size of buffer which
+ makes a copy to outq. */
+#define NGHTTP3_STREAM_MAX_COPY_THRES 128
+
+/* NGHTTP3_MIN_RBLEN is the minimum length of nghttp3_ringbuf */
+#define NGHTTP3_MIN_RBLEN 4
+
+int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
+ const nghttp3_stream_callbacks *callbacks,
+ nghttp3_objalloc *out_chunk_objalloc,
+ nghttp3_objalloc *stream_objalloc,
+ const nghttp3_mem *mem) {
+ nghttp3_stream *stream = nghttp3_objalloc_stream_get(stream_objalloc);
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ memset(stream, 0, sizeof(*stream));
+
+ stream->out_chunk_objalloc = out_chunk_objalloc;
+ stream->stream_objalloc = stream_objalloc;
+
+ nghttp3_tnode_init(&stream->node, stream_id, NGHTTP3_DEFAULT_URGENCY);
+
+ nghttp3_ringbuf_init(&stream->frq, 0, sizeof(nghttp3_frame_entry), mem);
+ nghttp3_ringbuf_init(&stream->chunks, 0, sizeof(nghttp3_buf), mem);
+ nghttp3_ringbuf_init(&stream->outq, 0, sizeof(nghttp3_typed_buf), mem);
+ nghttp3_ringbuf_init(&stream->inq, 0, sizeof(nghttp3_buf), mem);
+
+ nghttp3_qpack_stream_context_init(&stream->qpack_sctx, stream_id, mem);
+
+ stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX;
+ stream->mem = mem;
+ stream->tx.offset = 0;
+ stream->rx.http.status_code = -1;
+ stream->rx.http.content_length = -1;
+ stream->rx.http.pri = NGHTTP3_DEFAULT_URGENCY;
+ stream->error_code = NGHTTP3_H3_NO_ERROR;
+
+ if (callbacks) {
+ stream->callbacks = *callbacks;
+ }
+
+ *pstream = stream;
+
+ return 0;
+}
+
+static void delete_outq(nghttp3_ringbuf *outq, const nghttp3_mem *mem) {
+ nghttp3_typed_buf *tbuf;
+ size_t i, len = nghttp3_ringbuf_len(outq);
+
+ for (i = 0; i < len; ++i) {
+ tbuf = nghttp3_ringbuf_get(outq, i);
+ if (tbuf->type == NGHTTP3_BUF_TYPE_PRIVATE) {
+ nghttp3_buf_free(&tbuf->buf, mem);
+ }
+ }
+
+ nghttp3_ringbuf_free(outq);
+}
+
+static void delete_chunks(nghttp3_ringbuf *chunks, const nghttp3_mem *mem) {
+ nghttp3_buf *buf;
+ size_t i, len = nghttp3_ringbuf_len(chunks);
+
+ for (i = 0; i < len; ++i) {
+ buf = nghttp3_ringbuf_get(chunks, i);
+ nghttp3_buf_free(buf, mem);
+ }
+
+ nghttp3_ringbuf_free(chunks);
+}
+
+static void delete_out_chunks(nghttp3_ringbuf *chunks,
+ nghttp3_objalloc *out_chunk_objalloc,
+ const nghttp3_mem *mem) {
+ nghttp3_buf *buf;
+ size_t i, len = nghttp3_ringbuf_len(chunks);
+
+ for (i = 0; i < len; ++i) {
+ buf = nghttp3_ringbuf_get(chunks, i);
+
+ if (nghttp3_buf_cap(buf) == NGHTTP3_STREAM_MIN_CHUNK_SIZE) {
+ nghttp3_objalloc_chunk_release(out_chunk_objalloc,
+ (nghttp3_chunk *)(void *)buf->begin);
+ continue;
+ }
+
+ nghttp3_buf_free(buf, mem);
+ }
+
+ nghttp3_ringbuf_free(chunks);
+}
+
+static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) {
+ nghttp3_frame_entry *frent;
+ size_t i, len = nghttp3_ringbuf_len(frq);
+
+ for (i = 0; i < len; ++i) {
+ frent = nghttp3_ringbuf_get(frq, i);
+ switch (frent->fr.hd.type) {
+ case NGHTTP3_FRAME_HEADERS:
+ nghttp3_frame_headers_free(&frent->fr.headers, mem);
+ break;
+ default:
+ break;
+ }
+ }
+
+ nghttp3_ringbuf_free(frq);
+}
+
+void nghttp3_stream_del(nghttp3_stream *stream) {
+ if (stream == NULL) {
+ return;
+ }
+
+ nghttp3_qpack_stream_context_free(&stream->qpack_sctx);
+ delete_chunks(&stream->inq, stream->mem);
+ delete_outq(&stream->outq, stream->mem);
+ delete_out_chunks(&stream->chunks, stream->out_chunk_objalloc, stream->mem);
+ delete_frq(&stream->frq, stream->mem);
+ nghttp3_tnode_free(&stream->node);
+
+ nghttp3_objalloc_stream_release(stream->stream_objalloc, stream);
+}
+
+void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint) {
+ memset(rvint, 0, sizeof(*rvint));
+}
+
+void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate) {
+ memset(rstate, 0, sizeof(*rstate));
+}
+
+nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint,
+ const uint8_t *src, size_t srclen, int fin) {
+ size_t nread = 0;
+ size_t n;
+ size_t i;
+
+ assert(srclen > 0);
+
+ if (rvint->left == 0) {
+ assert(rvint->acc == 0);
+
+ rvint->left = nghttp3_get_varintlen(src);
+ if (rvint->left <= srclen) {
+ rvint->acc = nghttp3_get_varint(&nread, src);
+ rvint->left = 0;
+ return (nghttp3_ssize)nread;
+ }
+
+ if (fin) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ rvint->acc = nghttp3_get_varint_fb(src);
+ nread = 1;
+ ++src;
+ --srclen;
+ --rvint->left;
+ }
+
+ n = nghttp3_min(rvint->left, srclen);
+
+ for (i = 0; i < n; ++i) {
+ rvint->acc = (rvint->acc << 8) + src[i];
+ }
+
+ rvint->left -= n;
+ nread += n;
+
+ if (fin && rvint->left) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ return (nghttp3_ssize)nread;
+}
+
+int nghttp3_stream_frq_add(nghttp3_stream *stream,
+ const nghttp3_frame_entry *frent) {
+ nghttp3_ringbuf *frq = &stream->frq;
+ nghttp3_frame_entry *dest;
+ int rv;
+
+ if (nghttp3_ringbuf_full(frq)) {
+ size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, nghttp3_ringbuf_len(frq) * 2);
+ rv = nghttp3_ringbuf_reserve(frq, nlen);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ dest = nghttp3_ringbuf_push_back(frq);
+ *dest = *frent;
+
+ return 0;
+}
+
+int nghttp3_stream_fill_outq(nghttp3_stream *stream) {
+ nghttp3_ringbuf *frq = &stream->frq;
+ nghttp3_frame_entry *frent;
+ int data_eof;
+ int rv;
+
+ for (; nghttp3_ringbuf_len(frq) && !nghttp3_stream_outq_is_full(stream) &&
+ stream->unsent_bytes < NGHTTP3_MIN_UNSENT_BYTES;) {
+ frent = nghttp3_ringbuf_get(frq, 0);
+
+ switch (frent->fr.hd.type) {
+ case NGHTTP3_FRAME_SETTINGS:
+ rv = nghttp3_stream_write_settings(stream, frent);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGHTTP3_FRAME_HEADERS:
+ rv = nghttp3_stream_write_headers(stream, frent);
+ if (rv != 0) {
+ return rv;
+ }
+ nghttp3_frame_headers_free(&frent->fr.headers, stream->mem);
+ break;
+ case NGHTTP3_FRAME_DATA:
+ rv = nghttp3_stream_write_data(stream, &data_eof, frent);
+ if (rv != 0) {
+ return rv;
+ }
+ if (stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED) {
+ return 0;
+ }
+ if (!data_eof) {
+ return 0;
+ }
+ break;
+ case NGHTTP3_FRAME_GOAWAY:
+ rv = nghttp3_stream_write_goaway(stream, frent);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGHTTP3_FRAME_PRIORITY_UPDATE:
+ rv = nghttp3_stream_write_priority_update(stream, frent);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ default:
+ /* TODO Not implemented */
+ break;
+ }
+
+ nghttp3_ringbuf_pop_front(frq);
+ }
+
+ return 0;
+}
+
+static void typed_buf_shared_init(nghttp3_typed_buf *tbuf,
+ const nghttp3_buf *chunk) {
+ nghttp3_typed_buf_init(tbuf, chunk, NGHTTP3_BUF_TYPE_SHARED);
+ tbuf->buf.pos = tbuf->buf.last;
+}
+
+int nghttp3_stream_write_stream_type(nghttp3_stream *stream) {
+ size_t len = nghttp3_put_varintlen((int64_t)stream->type);
+ nghttp3_buf *chunk;
+ nghttp3_typed_buf tbuf;
+ int rv;
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type);
+ tbuf.buf.last = chunk->last;
+
+ return nghttp3_stream_outq_add(stream, &tbuf);
+}
+
+int nghttp3_stream_write_settings(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent) {
+ size_t len;
+ int rv;
+ nghttp3_buf *chunk;
+ nghttp3_typed_buf tbuf;
+ struct {
+ nghttp3_frame_settings settings;
+ nghttp3_settings_entry iv[15];
+ } fr;
+ nghttp3_settings_entry *iv;
+ nghttp3_settings *local_settings = frent->aux.settings.local_settings;
+
+ fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS;
+ fr.settings.niv = 3;
+ iv = &fr.settings.iv[0];
+
+ iv[0].id = NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE;
+ iv[0].value = local_settings->max_field_section_size;
+ iv[1].id = NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY;
+ iv[1].value = local_settings->qpack_max_dtable_capacity;
+ iv[2].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS;
+ iv[2].value = local_settings->qpack_blocked_streams;
+
+ if (local_settings->enable_connect_protocol) {
+ ++fr.settings.niv;
+ iv[3].id = NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL;
+ iv[3].value = 1;
+ }
+
+ len = nghttp3_frame_write_settings_len(&fr.settings.hd.length, &fr.settings);
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_frame_write_settings(chunk->last, &fr.settings);
+
+ tbuf.buf.last = chunk->last;
+
+ return nghttp3_stream_outq_add(stream, &tbuf);
+}
+
+int nghttp3_stream_write_goaway(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent) {
+ nghttp3_frame_goaway *fr = &frent->fr.goaway;
+ size_t len;
+ int rv;
+ nghttp3_buf *chunk;
+ nghttp3_typed_buf tbuf;
+
+ len = nghttp3_frame_write_goaway_len(&fr->hd.length, fr);
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_frame_write_goaway(chunk->last, fr);
+
+ tbuf.buf.last = chunk->last;
+
+ return nghttp3_stream_outq_add(stream, &tbuf);
+}
+
+int nghttp3_stream_write_priority_update(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent) {
+ nghttp3_frame_priority_update *fr = &frent->fr.priority_update;
+ size_t len;
+ int rv;
+ nghttp3_buf *chunk;
+ nghttp3_typed_buf tbuf;
+
+ len = nghttp3_frame_write_priority_update_len(&fr->hd.length, fr);
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_frame_write_priority_update(chunk->last, fr);
+
+ tbuf.buf.last = chunk->last;
+
+ return nghttp3_stream_outq_add(stream, &tbuf);
+}
+
+int nghttp3_stream_write_headers(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent) {
+ nghttp3_frame_headers *fr = &frent->fr.headers;
+ nghttp3_conn *conn = stream->conn;
+
+ assert(conn);
+
+ return nghttp3_stream_write_header_block(
+ stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf,
+ &conn->tx.qpack.ebuf, NGHTTP3_FRAME_HEADERS, fr->nva, fr->nvlen);
+}
+
+int nghttp3_stream_write_header_block(nghttp3_stream *stream,
+ nghttp3_qpack_encoder *qenc,
+ nghttp3_stream *qenc_stream,
+ nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ int64_t frame_type, const nghttp3_nv *nva,
+ size_t nvlen) {
+ nghttp3_buf pbuf;
+ int rv;
+ size_t len;
+ nghttp3_buf *chunk;
+ nghttp3_typed_buf tbuf;
+ nghttp3_frame_hd hd;
+ uint8_t raw_pbuf[16];
+ size_t pbuflen, rbuflen, ebuflen;
+
+ nghttp3_buf_wrap_init(&pbuf, raw_pbuf, sizeof(raw_pbuf));
+
+ rv = nghttp3_qpack_encoder_encode(qenc, &pbuf, rbuf, ebuf, stream->node.id,
+ nva, nvlen);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ pbuflen = nghttp3_buf_len(&pbuf);
+ rbuflen = nghttp3_buf_len(rbuf);
+ ebuflen = nghttp3_buf_len(ebuf);
+
+ hd.type = frame_type;
+ hd.length = (int64_t)(pbuflen + rbuflen);
+
+ len = nghttp3_frame_write_hd_len(&hd) + pbuflen;
+
+ if (rbuflen <= NGHTTP3_STREAM_MAX_COPY_THRES) {
+ len += rbuflen;
+ }
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_frame_write_hd(chunk->last, &hd);
+
+ chunk->last = nghttp3_cpymem(chunk->last, pbuf.pos, pbuflen);
+ nghttp3_buf_init(&pbuf);
+
+ if (rbuflen > NGHTTP3_STREAM_MAX_COPY_THRES) {
+ tbuf.buf.last = chunk->last;
+
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_typed_buf_init(&tbuf, rbuf, NGHTTP3_BUF_TYPE_PRIVATE);
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_buf_init(rbuf);
+ } else if (rbuflen) {
+ chunk->last = nghttp3_cpymem(chunk->last, rbuf->pos, rbuflen);
+ tbuf.buf.last = chunk->last;
+
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_buf_reset(rbuf);
+ }
+
+ if (ebuflen > NGHTTP3_STREAM_MAX_COPY_THRES) {
+ assert(qenc_stream);
+
+ nghttp3_typed_buf_init(&tbuf, ebuf, NGHTTP3_BUF_TYPE_PRIVATE);
+ rv = nghttp3_stream_outq_add(qenc_stream, &tbuf);
+ if (rv != 0) {
+ return rv;
+ }
+ nghttp3_buf_init(ebuf);
+ } else if (ebuflen) {
+ assert(qenc_stream);
+
+ rv = nghttp3_stream_ensure_chunk(qenc_stream, ebuflen);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ chunk = nghttp3_stream_get_chunk(qenc_stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_cpymem(chunk->last, ebuf->pos, ebuflen);
+ tbuf.buf.last = chunk->last;
+
+ rv = nghttp3_stream_outq_add(qenc_stream, &tbuf);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_buf_reset(ebuf);
+ }
+
+ assert(0 == nghttp3_buf_len(&pbuf));
+ assert(0 == nghttp3_buf_len(rbuf));
+ assert(0 == nghttp3_buf_len(ebuf));
+
+ return 0;
+
+fail:
+
+ return rv;
+}
+
+int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
+ nghttp3_frame_entry *frent) {
+ int rv;
+ size_t len;
+ nghttp3_typed_buf tbuf;
+ nghttp3_buf buf;
+ nghttp3_buf *chunk;
+ nghttp3_read_data_callback read_data = frent->aux.data.dr.read_data;
+ nghttp3_conn *conn = stream->conn;
+ int64_t datalen;
+ uint32_t flags = 0;
+ nghttp3_frame_hd hd;
+ nghttp3_vec vec[8];
+ nghttp3_vec *v;
+ nghttp3_ssize sveccnt;
+ size_t i;
+
+ assert(!(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED));
+ assert(read_data);
+ assert(conn);
+
+ *peof = 0;
+
+ sveccnt = read_data(conn, stream->node.id, vec, nghttp3_arraylen(vec), &flags,
+ conn->user_data, stream->user_data);
+ if (sveccnt < 0) {
+ if (sveccnt == NGHTTP3_ERR_WOULDBLOCK) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED;
+ return 0;
+ }
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ datalen = nghttp3_vec_len_varint(vec, (size_t)sveccnt);
+ if (datalen == -1) {
+ return NGHTTP3_ERR_STREAM_DATA_OVERFLOW;
+ }
+
+ assert(datalen || flags & NGHTTP3_DATA_FLAG_EOF);
+
+ if (flags & NGHTTP3_DATA_FLAG_EOF) {
+ *peof = 1;
+ if (!(flags & NGHTTP3_DATA_FLAG_NO_END_STREAM)) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM;
+ if (datalen == 0) {
+ if (nghttp3_stream_outq_write_done(stream)) {
+ /* If this is the last data and its is 0 length, we don't
+ need send DATA frame. We rely on the non-emptiness of
+ outq to schedule stream, so add empty tbuf to outq to
+ just send fin. */
+ nghttp3_buf_init(&buf);
+ nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_PRIVATE);
+ return nghttp3_stream_outq_add(stream, &tbuf);
+ }
+ return 0;
+ }
+ }
+
+ if (datalen == 0) {
+ /* We are going to send more frames, but no DATA frame this
+ time. */
+ return 0;
+ }
+ }
+
+ hd.type = NGHTTP3_FRAME_DATA;
+ hd.length = datalen;
+
+ len = nghttp3_frame_write_hd_len(&hd);
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_frame_write_hd(chunk->last, &hd);
+
+ tbuf.buf.last = chunk->last;
+
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (datalen) {
+ for (i = 0; i < (size_t)sveccnt; ++i) {
+ v = &vec[i];
+ if (v->len == 0) {
+ continue;
+ }
+ nghttp3_buf_wrap_init(&buf, v->base, v->len);
+ buf.last = buf.end;
+ nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_ALIEN);
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream) {
+ nghttp3_qpack_decoder *qdec;
+ nghttp3_buf *chunk;
+ int rv;
+ nghttp3_typed_buf tbuf;
+ size_t len;
+
+ assert(stream->conn);
+ assert(stream->conn->tx.qdec == stream);
+
+ qdec = &stream->conn->qdec;
+
+ assert(qdec);
+
+ len = nghttp3_qpack_decoder_get_decoder_streamlen(qdec);
+ if (len == 0) {
+ return 0;
+ }
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ nghttp3_qpack_decoder_write_decoder(qdec, chunk);
+
+ tbuf.buf.last = chunk->last;
+
+ return nghttp3_stream_outq_add(stream, &tbuf);
+}
+
+int nghttp3_stream_outq_is_full(nghttp3_stream *stream) {
+ /* TODO Verify that the limit is reasonable. */
+ return nghttp3_ringbuf_len(&stream->outq) >= 1024;
+}
+
+int nghttp3_stream_outq_add(nghttp3_stream *stream,
+ const nghttp3_typed_buf *tbuf) {
+ nghttp3_ringbuf *outq = &stream->outq;
+ int rv;
+ nghttp3_typed_buf *dest;
+ size_t len = nghttp3_ringbuf_len(outq);
+ size_t buflen = nghttp3_buf_len(&tbuf->buf);
+
+ if (buflen > NGHTTP3_MAX_VARINT - stream->tx.offset) {
+ return NGHTTP3_ERR_STREAM_DATA_OVERFLOW;
+ }
+
+ stream->tx.offset += buflen;
+ stream->unsent_bytes += buflen;
+
+ if (len) {
+ dest = nghttp3_ringbuf_get(outq, len - 1);
+ if (dest->type == tbuf->type && dest->type == NGHTTP3_BUF_TYPE_SHARED &&
+ dest->buf.begin == tbuf->buf.begin && dest->buf.last == tbuf->buf.pos) {
+ /* If we have already written last entry, adjust outq_idx and
+ offset so that this entry is eligible to send. */
+ if (len == stream->outq_idx) {
+ --stream->outq_idx;
+ stream->outq_offset = nghttp3_buf_len(&dest->buf);
+ }
+
+ dest->buf.last = tbuf->buf.last;
+ /* TODO Is this required? */
+ dest->buf.end = tbuf->buf.end;
+
+ return 0;
+ }
+ }
+
+ if (nghttp3_ringbuf_full(outq)) {
+ size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, len * 2);
+ rv = nghttp3_ringbuf_reserve(outq, nlen);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ dest = nghttp3_ringbuf_push_back(outq);
+ *dest = *tbuf;
+
+ return 0;
+}
+
+int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need) {
+ nghttp3_ringbuf *chunks = &stream->chunks;
+ nghttp3_buf *chunk;
+ size_t len = nghttp3_ringbuf_len(chunks);
+ uint8_t *p;
+ int rv;
+ size_t n = NGHTTP3_STREAM_MIN_CHUNK_SIZE;
+
+ if (len) {
+ chunk = nghttp3_ringbuf_get(chunks, len - 1);
+ if (nghttp3_buf_left(chunk) >= need) {
+ return 0;
+ }
+ }
+
+ for (; n < need; n *= 2)
+ ;
+
+ if (n == NGHTTP3_STREAM_MIN_CHUNK_SIZE) {
+ p = (uint8_t *)nghttp3_objalloc_chunk_len_get(stream->out_chunk_objalloc,
+ n);
+ } else {
+ p = nghttp3_mem_malloc(stream->mem, n);
+ }
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ if (nghttp3_ringbuf_full(chunks)) {
+ size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, len * 2);
+ rv = nghttp3_ringbuf_reserve(chunks, nlen);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ chunk = nghttp3_ringbuf_push_back(chunks);
+ nghttp3_buf_wrap_init(chunk, p, n);
+
+ return 0;
+}
+
+nghttp3_buf *nghttp3_stream_get_chunk(nghttp3_stream *stream) {
+ nghttp3_ringbuf *chunks = &stream->chunks;
+ size_t len = nghttp3_ringbuf_len(chunks);
+
+ assert(len);
+
+ return nghttp3_ringbuf_get(chunks, len - 1);
+}
+
+int nghttp3_stream_is_blocked(nghttp3_stream *stream) {
+ return (stream->flags & NGHTTP3_STREAM_FLAG_FC_BLOCKED) ||
+ (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_WR) ||
+ (stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED);
+}
+
+int nghttp3_stream_require_schedule(nghttp3_stream *stream) {
+ return (!nghttp3_stream_outq_write_done(stream) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_FC_BLOCKED) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_SHUT_WR)) ||
+ (nghttp3_ringbuf_len(&stream->frq) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED));
+}
+
+nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin,
+ nghttp3_vec *vec, size_t veccnt) {
+ nghttp3_ringbuf *outq = &stream->outq;
+ size_t len = nghttp3_ringbuf_len(outq);
+ size_t i = stream->outq_idx;
+ uint64_t offset = stream->outq_offset;
+ size_t buflen;
+ nghttp3_vec *vbegin = vec, *vend = vec + veccnt;
+ nghttp3_typed_buf *tbuf;
+
+ assert(veccnt > 0);
+
+ if (i < len) {
+ tbuf = nghttp3_ringbuf_get(outq, i);
+ buflen = nghttp3_buf_len(&tbuf->buf);
+
+ if (offset < buflen) {
+ vec->base = tbuf->buf.pos + offset;
+ vec->len = (size_t)(buflen - offset);
+ ++vec;
+ } else {
+ /* This is the only case that satisfies offset >= buflen */
+ assert(0 == offset);
+ assert(0 == buflen);
+ }
+
+ ++i;
+
+ for (; i < len && vec != vend; ++i, ++vec) {
+ tbuf = nghttp3_ringbuf_get(outq, i);
+ vec->base = tbuf->buf.pos;
+ vec->len = nghttp3_buf_len(&tbuf->buf);
+ }
+ }
+
+ /* TODO Rework this if we have finished implementing HTTP
+ messaging */
+ *pfin = nghttp3_ringbuf_len(&stream->frq) == 0 && i == len &&
+ (stream->flags & NGHTTP3_STREAM_FLAG_WRITE_END_STREAM);
+
+ return vec - vbegin;
+}
+
+int nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n) {
+ nghttp3_ringbuf *outq = &stream->outq;
+ size_t i;
+ size_t len = nghttp3_ringbuf_len(outq);
+ uint64_t offset = stream->outq_offset + n;
+ size_t buflen;
+ nghttp3_typed_buf *tbuf;
+
+ for (i = stream->outq_idx; i < len; ++i) {
+ tbuf = nghttp3_ringbuf_get(outq, i);
+ buflen = nghttp3_buf_len(&tbuf->buf);
+ if (offset >= buflen) {
+ offset -= buflen;
+ continue;
+ }
+
+ break;
+ }
+
+ assert(i < len || offset == 0);
+
+ stream->unsent_bytes -= n;
+ stream->outq_idx = i;
+ stream->outq_offset = offset;
+
+ return 0;
+}
+
+int nghttp3_stream_outq_write_done(nghttp3_stream *stream) {
+ nghttp3_ringbuf *outq = &stream->outq;
+ size_t len = nghttp3_ringbuf_len(outq);
+
+ return len == 0 || stream->outq_idx >= len;
+}
+
+static int stream_pop_outq_entry(nghttp3_stream *stream,
+ nghttp3_typed_buf *tbuf) {
+ nghttp3_ringbuf *chunks = &stream->chunks;
+ nghttp3_buf *chunk;
+
+ switch (tbuf->type) {
+ case NGHTTP3_BUF_TYPE_PRIVATE:
+ nghttp3_buf_free(&tbuf->buf, stream->mem);
+ break;
+ case NGHTTP3_BUF_TYPE_ALIEN:
+ break;
+ case NGHTTP3_BUF_TYPE_SHARED:
+ assert(nghttp3_ringbuf_len(chunks));
+
+ chunk = nghttp3_ringbuf_get(chunks, 0);
+
+ assert(chunk->begin == tbuf->buf.begin);
+ assert(chunk->end == tbuf->buf.end);
+
+ if (chunk->last == tbuf->buf.last) {
+ if (nghttp3_buf_cap(chunk) == NGHTTP3_STREAM_MIN_CHUNK_SIZE) {
+ nghttp3_objalloc_chunk_release(stream->out_chunk_objalloc,
+ (nghttp3_chunk *)(void *)chunk->begin);
+ } else {
+ nghttp3_buf_free(chunk, stream->mem);
+ }
+ nghttp3_ringbuf_pop_front(chunks);
+ }
+ break;
+ default:
+ nghttp3_unreachable();
+ };
+
+ nghttp3_ringbuf_pop_front(&stream->outq);
+
+ return 0;
+}
+
+int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n) {
+ nghttp3_ringbuf *outq = &stream->outq;
+ uint64_t offset = stream->ack_offset + n;
+ size_t buflen;
+ size_t npopped = 0;
+ uint64_t nack;
+ nghttp3_typed_buf *tbuf;
+ int rv;
+
+ for (; nghttp3_ringbuf_len(outq);) {
+ tbuf = nghttp3_ringbuf_get(outq, 0);
+ buflen = nghttp3_buf_len(&tbuf->buf);
+
+ if (tbuf->type == NGHTTP3_BUF_TYPE_ALIEN) {
+ nack = nghttp3_min(offset, (uint64_t)buflen) - stream->ack_done;
+ if (stream->callbacks.acked_data) {
+ rv = stream->callbacks.acked_data(stream, stream->node.id, nack,
+ stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ }
+ stream->ack_done += nack;
+ }
+
+ if (offset >= buflen) {
+ rv = stream_pop_outq_entry(stream, tbuf);
+ if (rv != 0) {
+ return rv;
+ }
+
+ offset -= buflen;
+ ++npopped;
+ stream->ack_done = 0;
+
+ if (stream->outq_idx + 1 == npopped) {
+ stream->outq_offset = 0;
+ break;
+ }
+
+ continue;
+ }
+
+ break;
+ }
+
+ assert(stream->outq_idx + 1 >= npopped);
+ if (stream->outq_idx >= npopped) {
+ stream->outq_idx -= npopped;
+ } else {
+ stream->outq_idx = 0;
+ }
+
+ stream->ack_offset = offset;
+
+ return 0;
+}
+
+int nghttp3_stream_buffer_data(nghttp3_stream *stream, const uint8_t *data,
+ size_t datalen) {
+ nghttp3_ringbuf *inq = &stream->inq;
+ size_t len = nghttp3_ringbuf_len(inq);
+ nghttp3_buf *buf;
+ size_t nwrite;
+ uint8_t *rawbuf;
+ size_t bufleft;
+ int rv;
+
+ if (len) {
+ buf = nghttp3_ringbuf_get(inq, len - 1);
+ bufleft = nghttp3_buf_left(buf);
+ nwrite = nghttp3_min(datalen, bufleft);
+ buf->last = nghttp3_cpymem(buf->last, data, nwrite);
+ data += nwrite;
+ datalen -= nwrite;
+ }
+
+ for (; datalen;) {
+ if (nghttp3_ringbuf_full(inq)) {
+ size_t nlen =
+ nghttp3_max(NGHTTP3_MIN_RBLEN, nghttp3_ringbuf_len(inq) * 2);
+ rv = nghttp3_ringbuf_reserve(inq, nlen);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ rawbuf = nghttp3_mem_malloc(stream->mem, 16384);
+ if (rawbuf == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ buf = nghttp3_ringbuf_push_back(inq);
+ nghttp3_buf_wrap_init(buf, rawbuf, 16384);
+ bufleft = nghttp3_buf_left(buf);
+ nwrite = nghttp3_min(datalen, bufleft);
+ buf->last = nghttp3_cpymem(buf->last, data, nwrite);
+ data += nwrite;
+ datalen -= nwrite;
+ }
+
+ return 0;
+}
+
+size_t nghttp3_stream_get_buffered_datalen(nghttp3_stream *stream) {
+ nghttp3_ringbuf *inq = &stream->inq;
+ size_t len = nghttp3_ringbuf_len(inq);
+ size_t i, n = 0;
+ nghttp3_buf *buf;
+
+ for (i = 0; i < len; ++i) {
+ buf = nghttp3_ringbuf_get(inq, i);
+ n += nghttp3_buf_len(buf);
+ }
+
+ return n;
+}
+
+int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
+ nghttp3_stream_http_event event) {
+ int rv;
+
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_NONE:
+ return NGHTTP3_ERR_H3_INTERNAL_ERROR;
+ case NGHTTP3_HTTP_STATE_REQ_INITIAL:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_END:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ /* TODO Better to check status code */
+ if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_DATA_BEGIN:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_MSG_END:
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_DATA_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_REQ_DATA_END:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_DATA_BEGIN:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ /* TODO Better to check status code */
+ if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_MSG_END:
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_END:
+ if (event != NGHTTP3_HTTP_EVENT_MSG_END) {
+ /* TODO Should ignore unexpected frame in this state as per
+ spec. */
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_REQ_END:
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ case NGHTTP3_HTTP_STATE_RESP_INITIAL:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_BEGIN) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_END:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ if (stream->rx.http.status_code == -1) {
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN;
+ return 0;
+ }
+ if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) &&
+ stream->rx.http.status_code / 100 == 2) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_DATA_BEGIN:
+ if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_MSG_END:
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_DATA_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_RESP_DATA_END:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_DATA_BEGIN:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) &&
+ stream->rx.http.status_code / 100 == 2) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_MSG_END:
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_END:
+ if (event != NGHTTP3_HTTP_EVENT_MSG_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_RESP_END:
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ default:
+ nghttp3_unreachable();
+ }
+}
+
+int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream) {
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ return 0;
+ default:
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+}
+
+int nghttp3_stream_uni(int64_t stream_id) { return (stream_id & 0x2) != 0; }
+
+int nghttp3_client_stream_bidi(int64_t stream_id) {
+ return (stream_id & 0x3) == 0;
+}
+
+int nghttp3_client_stream_uni(int64_t stream_id) {
+ return (stream_id & 0x3) == 0x2;
+}
+
+int nghttp3_server_stream_uni(int64_t stream_id) {
+ return (stream_id & 0x3) == 0x3;
+}
diff --git a/lib/nghttp3_stream.h b/lib/nghttp3_stream.h
new file mode 100644
index 0000000..be8881f
--- /dev/null
+++ b/lib/nghttp3_stream.h
@@ -0,0 +1,396 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_STREAM_H
+#define NGHTTP3_STREAM_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_map.h"
+#include "nghttp3_tnode.h"
+#include "nghttp3_ringbuf.h"
+#include "nghttp3_buf.h"
+#include "nghttp3_frame.h"
+#include "nghttp3_qpack.h"
+#include "nghttp3_objalloc.h"
+
+#define NGHTTP3_STREAM_MIN_CHUNK_SIZE 256
+
+/* NGHTTP3_MIN_UNSENT_BYTES is the minimum unsent bytes which is large
+ enough to fill outgoing single QUIC packet. */
+#define NGHTTP3_MIN_UNSENT_BYTES 4096
+
+/* NGHTTP3_STREAM_MIN_WRITELEN is the minimum length of write to cause
+ the stream to reschedule. */
+#define NGHTTP3_STREAM_MIN_WRITELEN 800
+
+/* nghttp3_stream_type is unidirectional stream type. */
+typedef enum nghttp3_stream_type {
+ NGHTTP3_STREAM_TYPE_CONTROL = 0x00,
+ NGHTTP3_STREAM_TYPE_PUSH = 0x01,
+ NGHTTP3_STREAM_TYPE_QPACK_ENCODER = 0x02,
+ NGHTTP3_STREAM_TYPE_QPACK_DECODER = 0x03,
+ NGHTTP3_STREAM_TYPE_UNKNOWN = UINT64_MAX,
+} nghttp3_stream_type;
+
+typedef enum nghttp3_ctrl_stream_state {
+ NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE,
+ NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH,
+ NGHTTP3_CTRL_STREAM_STATE_SETTINGS,
+ NGHTTP3_CTRL_STREAM_STATE_GOAWAY,
+ NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID,
+ NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME,
+ NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID,
+ NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE,
+ NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID,
+ NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE,
+} nghttp3_ctrl_stream_state;
+
+typedef enum nghttp3_req_stream_state {
+ NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE,
+ NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH,
+ NGHTTP3_REQ_STREAM_STATE_DATA,
+ NGHTTP3_REQ_STREAM_STATE_HEADERS,
+ NGHTTP3_REQ_STREAM_STATE_IGN_FRAME,
+ NGHTTP3_REQ_STREAM_STATE_IGN_REST,
+} nghttp3_req_stream_state;
+
+typedef struct nghttp3_varint_read_state {
+ int64_t acc;
+ size_t left;
+} nghttp3_varint_read_state;
+
+typedef struct nghttp3_stream_read_state {
+ nghttp3_varint_read_state rvint;
+ nghttp3_frame fr;
+ int state;
+ int64_t left;
+} nghttp3_stream_read_state;
+
+/* NGHTTP3_STREAM_FLAG_NONE indicates that no flag is set. */
+#define NGHTTP3_STREAM_FLAG_NONE 0x0000u
+/* NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED is set when a unidirectional
+ stream type is identified. */
+#define NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED 0x0001u
+/* NGHTTP3_STREAM_FLAG_FC_BLOCKED indicates that stream is blocked by
+ QUIC flow control. */
+#define NGHTTP3_STREAM_FLAG_FC_BLOCKED 0x0002u
+/* NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED indicates that application is
+ temporarily unable to provide data. */
+#define NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED 0x0004u
+/* NGHTTP3_STREAM_FLAG_WRITE_END_STREAM indicates that application
+ finished to feed outgoing data. */
+#define NGHTTP3_STREAM_FLAG_WRITE_END_STREAM 0x0008u
+/* NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED indicates that stream is
+ blocked due to QPACK decoding. */
+#define NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED 0x0010u
+/* NGHTTP3_STREAM_FLAG_READ_EOF indicates that remote endpoint sent
+ fin. */
+#define NGHTTP3_STREAM_FLAG_READ_EOF 0x0020u
+/* NGHTTP3_STREAM_FLAG_CLOSED indicates that QUIC stream was closed.
+ nghttp3_stream object can still alive because it might be blocked
+ by QPACK decoder. */
+#define NGHTTP3_STREAM_FLAG_CLOSED 0x0040u
+/* NGHTTP3_STREAM_FLAG_SHUT_WR indicates that any further write
+ operation to a stream is prohibited. */
+#define NGHTTP3_STREAM_FLAG_SHUT_WR 0x0100u
+/* NGHTTP3_STREAM_FLAG_SHUT_RD indicates that a read-side stream is
+ closed abruptly and any incoming and pending stream data is just
+ discarded for a stream. */
+#define NGHTTP3_STREAM_FLAG_SHUT_RD 0x0200u
+/* NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET indicates that server
+ overrides stream priority. */
+#define NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET 0x0400u
+/* NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED indicates that server
+ received PRIORITY_UPDATE frame for this stream. */
+#define NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED 0x0800u
+/* NGHTTP3_STREAM_FLAG_HTTP_ERROR indicates that
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER error is encountered while
+ processing incoming HTTP fields. */
+#define NGHTTP3_STREAM_FLAG_HTTP_ERROR 0x1000u
+
+typedef enum nghttp3_stream_http_state {
+ NGHTTP3_HTTP_STATE_NONE,
+ NGHTTP3_HTTP_STATE_REQ_INITIAL,
+ NGHTTP3_HTTP_STATE_REQ_BEGIN,
+ NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN,
+ NGHTTP3_HTTP_STATE_REQ_HEADERS_END,
+ NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN,
+ NGHTTP3_HTTP_STATE_REQ_DATA_END,
+ NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN,
+ NGHTTP3_HTTP_STATE_REQ_TRAILERS_END,
+ NGHTTP3_HTTP_STATE_REQ_END,
+ NGHTTP3_HTTP_STATE_RESP_INITIAL,
+ NGHTTP3_HTTP_STATE_RESP_BEGIN,
+ NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN,
+ NGHTTP3_HTTP_STATE_RESP_HEADERS_END,
+ NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN,
+ NGHTTP3_HTTP_STATE_RESP_DATA_END,
+ NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN,
+ NGHTTP3_HTTP_STATE_RESP_TRAILERS_END,
+ NGHTTP3_HTTP_STATE_RESP_END,
+} nghttp3_stream_http_state;
+
+typedef enum nghttp3_stream_http_event {
+ NGHTTP3_HTTP_EVENT_DATA_BEGIN,
+ NGHTTP3_HTTP_EVENT_DATA_END,
+ NGHTTP3_HTTP_EVENT_HEADERS_BEGIN,
+ NGHTTP3_HTTP_EVENT_HEADERS_END,
+ NGHTTP3_HTTP_EVENT_MSG_END,
+} nghttp3_stream_http_event;
+
+typedef struct nghttp3_stream nghttp3_stream;
+
+/*
+ * nghttp3_stream_acked_data is a callback function which is invoked
+ * when data sent on stream denoted by |stream_id| supplied from
+ * application is acknowledged by remote endpoint. The number of
+ * bytes acknowledged is given in |datalen|.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning NGHTTP3_ERR_CALLBACK_FAILURE will return to the caller
+ * immediately. Any values other than 0 is treated as
+ * NGHTTP3_ERR_CALLBACK_FAILURE.
+ */
+typedef int (*nghttp3_stream_acked_data)(nghttp3_stream *stream,
+ int64_t stream_id, uint64_t datalen,
+ void *user_data);
+
+typedef struct nghttp3_stream_callbacks {
+ nghttp3_stream_acked_data acked_data;
+} nghttp3_stream_callbacks;
+
+typedef struct nghttp3_http_state {
+ /* status_code is HTTP status code received. This field is used
+ if connection is initialized as client. */
+ int32_t status_code;
+ /* content_length is the value of received content-length header
+ field. */
+ int64_t content_length;
+ /* recv_content_length is the number of body bytes received so
+ far. */
+ int64_t recv_content_length;
+ uint32_t flags;
+ /* pri is a stream priority produced by nghttp3_pri_to_uint8. */
+ uint8_t pri;
+} nghttp3_http_state;
+
+struct nghttp3_stream {
+ union {
+ struct {
+ const nghttp3_mem *mem;
+ nghttp3_objalloc *out_chunk_objalloc;
+ nghttp3_objalloc *stream_objalloc;
+ nghttp3_tnode node;
+ nghttp3_pq_entry qpack_blocked_pe;
+ nghttp3_stream_callbacks callbacks;
+ nghttp3_ringbuf frq;
+ nghttp3_ringbuf chunks;
+ nghttp3_ringbuf outq;
+ /* inq stores the stream raw data which cannot be read because
+ stream is blocked by QPACK decoder. */
+ nghttp3_ringbuf inq;
+ nghttp3_qpack_stream_context qpack_sctx;
+ /* conn is a reference to underlying connection. It could be NULL
+ if stream is not a request stream. */
+ nghttp3_conn *conn;
+ void *user_data;
+ /* unsent_bytes is the number of bytes in outq not written yet */
+ uint64_t unsent_bytes;
+ /* outq_idx is an index into outq where next write is made. */
+ size_t outq_idx;
+ /* outq_offset is write offset relative to the element at outq_idx
+ in outq. */
+ uint64_t outq_offset;
+ /* ack_offset is offset acknowledged by peer relative to the first
+ element in outq. */
+ uint64_t ack_offset;
+ /* ack_done is the number of bytes notified to an application that
+ they are acknowledged inside the first outq element if it is of
+ type NGHTTP3_BUF_TYPE_ALIEN. */
+ uint64_t ack_done;
+ uint64_t unscheduled_nwrite;
+ nghttp3_stream_type type;
+ nghttp3_stream_read_state rstate;
+ /* error_code indicates the reason of closure of this stream. */
+ uint64_t error_code;
+
+ struct {
+ uint64_t offset;
+ nghttp3_stream_http_state hstate;
+ } tx;
+
+ struct {
+ nghttp3_stream_http_state hstate;
+ nghttp3_http_state http;
+ } rx;
+
+ uint16_t flags;
+ };
+
+ nghttp3_opl_entry oplent;
+ };
+};
+
+nghttp3_objalloc_def(stream, nghttp3_stream, oplent);
+
+typedef struct nghttp3_frame_entry {
+ nghttp3_frame fr;
+ union {
+ struct {
+ nghttp3_settings *local_settings;
+ } settings;
+ struct {
+ nghttp3_data_reader dr;
+ } data;
+ } aux;
+} nghttp3_frame_entry;
+
+int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
+ const nghttp3_stream_callbacks *callbacks,
+ nghttp3_objalloc *out_chunk_objalloc,
+ nghttp3_objalloc *stream_objalloc,
+ const nghttp3_mem *mem);
+
+void nghttp3_stream_del(nghttp3_stream *stream);
+
+void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint);
+
+void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate);
+
+nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint,
+ const uint8_t *src, size_t srclen, int fin);
+
+int nghttp3_stream_frq_add(nghttp3_stream *stream,
+ const nghttp3_frame_entry *frent);
+
+int nghttp3_stream_fill_outq(nghttp3_stream *stream);
+
+int nghttp3_stream_write_stream_type(nghttp3_stream *stream);
+
+nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin,
+ nghttp3_vec *vec, size_t veccnt);
+
+int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream);
+
+int nghttp3_stream_outq_is_full(nghttp3_stream *stream);
+
+int nghttp3_stream_outq_add(nghttp3_stream *stream,
+ const nghttp3_typed_buf *tbuf);
+
+int nghttp3_stream_write_headers(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent);
+
+int nghttp3_stream_write_header_block(nghttp3_stream *stream,
+ nghttp3_qpack_encoder *qenc,
+ nghttp3_stream *qenc_stream,
+ nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ int64_t frame_type, const nghttp3_nv *nva,
+ size_t nvlen);
+
+int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
+ nghttp3_frame_entry *frent);
+
+int nghttp3_stream_write_settings(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent);
+
+int nghttp3_stream_write_goaway(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent);
+
+int nghttp3_stream_write_priority_update(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent);
+
+int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need);
+
+nghttp3_buf *nghttp3_stream_get_chunk(nghttp3_stream *stream);
+
+int nghttp3_stream_is_blocked(nghttp3_stream *stream);
+
+int nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n);
+
+/*
+ * nghttp3_stream_outq_write_done returns nonzero if all contents in
+ * outq have been written.
+ */
+int nghttp3_stream_outq_write_done(nghttp3_stream *stream);
+
+int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n);
+
+/*
+ * nghttp3_stream_is_active returns nonzero if |stream| is active. In
+ * other words, it has something to send. This function does not take
+ * into account its descendants.
+ */
+int nghttp3_stream_is_active(nghttp3_stream *stream);
+
+/*
+ * nghttp3_stream_require_schedule returns nonzero if |stream| should
+ * be scheduled. In other words, |stream| or its descendants have
+ * something to send.
+ */
+int nghttp3_stream_require_schedule(nghttp3_stream *stream);
+
+int nghttp3_stream_buffer_data(nghttp3_stream *stream, const uint8_t *src,
+ size_t srclen);
+
+size_t nghttp3_stream_get_buffered_datalen(nghttp3_stream *stream);
+
+int nghttp3_stream_ensure_qpack_stream_context(nghttp3_stream *stream);
+
+void nghttp3_stream_delete_qpack_stream_context(nghttp3_stream *stream);
+
+int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
+ nghttp3_stream_http_event event);
+
+int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream);
+
+/*
+ * nghttp3_stream_uni returns nonzero if stream identified by
+ * |stream_id| is unidirectional.
+ */
+int nghttp3_stream_uni(int64_t stream_id);
+
+/*
+ * nghttp3_client_stream_bidi returns nonzero if stream identified by
+ * |stream_id| is client initiated bidirectional stream.
+ */
+int nghttp3_client_stream_bidi(int64_t stream_id);
+
+/*
+ * nghttp3_client_stream_uni returns nonzero if stream identified by
+ * |stream_id| is client initiated unidirectional stream.
+ */
+int nghttp3_client_stream_uni(int64_t stream_id);
+
+/*
+ * nghttp3_server_stream_uni returns nonzero if stream identified by
+ * |stream_id| is server initiated unidirectional stream.
+ */
+int nghttp3_server_stream_uni(int64_t stream_id);
+
+#endif /* NGHTTP3_STREAM_H */
diff --git a/lib/nghttp3_tnode.c b/lib/nghttp3_tnode.c
new file mode 100644
index 0000000..2fe7f3f
--- /dev/null
+++ b/lib/nghttp3_tnode.c
@@ -0,0 +1,97 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_tnode.h"
+
+#include <assert.h>
+
+#include "nghttp3_macro.h"
+#include "nghttp3_stream.h"
+#include "nghttp3_conn.h"
+#include "nghttp3_conv.h"
+
+void nghttp3_tnode_init(nghttp3_tnode *tnode, int64_t id, uint8_t pri) {
+ assert(nghttp3_pri_uint8_urgency(pri) < NGHTTP3_URGENCY_LEVELS);
+
+ tnode->pe.index = NGHTTP3_PQ_BAD_INDEX;
+ tnode->id = id;
+ tnode->cycle = 0;
+ tnode->pri = pri;
+}
+
+void nghttp3_tnode_free(nghttp3_tnode *tnode) { (void)tnode; }
+
+static void tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq) {
+ assert(tnode->pe.index != NGHTTP3_PQ_BAD_INDEX);
+
+ nghttp3_pq_remove(pq, &tnode->pe);
+ tnode->pe.index = NGHTTP3_PQ_BAD_INDEX;
+}
+
+void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq) {
+ if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) {
+ return;
+ }
+
+ tnode_unschedule(tnode, pq);
+}
+
+static uint64_t pq_get_first_cycle(nghttp3_pq *pq) {
+ nghttp3_tnode *top;
+
+ if (nghttp3_pq_empty(pq)) {
+ return 0;
+ }
+
+ top = nghttp3_struct_of(nghttp3_pq_top(pq), nghttp3_tnode, pe);
+ return top->cycle;
+}
+
+int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq,
+ uint64_t nwrite) {
+ uint64_t penalty = nwrite / NGHTTP3_STREAM_MIN_WRITELEN;
+
+ if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) {
+ tnode->cycle = pq_get_first_cycle(pq) +
+ ((nwrite == 0 || !nghttp3_pri_uint8_inc(tnode->pri))
+ ? 0
+ : nghttp3_max(1, penalty));
+ } else if (nwrite > 0) {
+ if (!nghttp3_pri_uint8_inc(tnode->pri) || nghttp3_pq_size(pq) == 1) {
+ return 0;
+ }
+
+ nghttp3_pq_remove(pq, &tnode->pe);
+ tnode->pe.index = NGHTTP3_PQ_BAD_INDEX;
+ tnode->cycle += nghttp3_max(1, penalty);
+ } else {
+ return 0;
+ }
+
+ return nghttp3_pq_push(pq, &tnode->pe);
+}
+
+int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode) {
+ return tnode->pe.index != NGHTTP3_PQ_BAD_INDEX;
+}
diff --git a/lib/nghttp3_tnode.h b/lib/nghttp3_tnode.h
new file mode 100644
index 0000000..f97120a
--- /dev/null
+++ b/lib/nghttp3_tnode.h
@@ -0,0 +1,66 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_TNODE_H
+#define NGHTTP3_TNODE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_pq.h"
+
+#define NGHTTP3_TNODE_MAX_CYCLE_GAP (1llu << 24)
+
+typedef struct nghttp3_tnode {
+ nghttp3_pq_entry pe;
+ size_t num_children;
+ int64_t id;
+ uint64_t cycle;
+ /* pri is a stream priority produced by nghttp3_pri_to_uint8. */
+ uint8_t pri;
+} nghttp3_tnode;
+
+void nghttp3_tnode_init(nghttp3_tnode *tnode, int64_t id, uint8_t pri);
+
+void nghttp3_tnode_free(nghttp3_tnode *tnode);
+
+void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq);
+
+/*
+ * nghttp3_tnode_schedule schedules |tnode| using |nwrite| as penalty.
+ * If |tnode| has already been scheduled, it is rescheduled by the
+ * amount of |nwrite|.
+ */
+int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq,
+ uint64_t nwrite);
+
+/*
+ * nghttp3_tnode_is_scheduled returns nonzero if |tnode| is scheduled.
+ */
+int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode);
+
+#endif /* NGHTTP3_TNODE_H */
diff --git a/lib/nghttp3_unreachable.c b/lib/nghttp3_unreachable.c
new file mode 100644
index 0000000..6fea89b
--- /dev/null
+++ b/lib/nghttp3_unreachable.c
@@ -0,0 +1,72 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_unreachable.h"
+
+#include <stdio.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <stdlib.h>
+#ifdef WIN32
+# include <io.h>
+#endif /* WIN32 */
+
+void nghttp3_unreachable_fail(const char *file, int line, const char *func) {
+ char *buf;
+ size_t buflen;
+ int rv;
+
+#define NGHTTP3_UNREACHABLE_TEMPLATE "%s:%d %s: Unreachable.\n"
+
+ rv = snprintf(NULL, 0, NGHTTP3_UNREACHABLE_TEMPLATE, file, line, func);
+ if (rv < 0) {
+ abort();
+ }
+
+ /* here we explicitly use system malloc */
+ buflen = (size_t)rv + 1;
+ buf = malloc(buflen);
+ if (buf == NULL) {
+ abort();
+ }
+
+ rv = snprintf(buf, buflen, NGHTTP3_UNREACHABLE_TEMPLATE, file, line, func);
+ if (rv < 0) {
+ abort();
+ }
+
+#ifndef WIN32
+ while (write(STDERR_FILENO, buf, (size_t)rv) == -1 && errno == EINTR)
+ ;
+#else /* WIN32 */
+ _write(_fileno(stderr), buf, (unsigned int)rv);
+#endif /* WIN32 */
+
+ free(buf);
+
+ abort();
+}
diff --git a/lib/nghttp3_unreachable.h b/lib/nghttp3_unreachable.h
new file mode 100644
index 0000000..c3520a4
--- /dev/null
+++ b/lib/nghttp3_unreachable.h
@@ -0,0 +1,47 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2022 nghttp3 contributors
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_UNREACHABLE_H
+#define NGHTTP3_UNREACHABLE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#define nghttp3_unreachable() \
+ nghttp3_unreachable_fail(__FILE__, __LINE__, __func__)
+
+#ifdef _MSC_VER
+__declspec(noreturn)
+#endif /* _MSC_VER */
+ void nghttp3_unreachable_fail(const char *file, int line, const char *func)
+#ifndef _MSC_VER
+ __attribute__((noreturn))
+#endif /* !_MSC_VER */
+ ;
+
+#endif /* NGHTTP3_UNREACHABLE_H */
diff --git a/lib/nghttp3_vec.c b/lib/nghttp3_vec.c
new file mode 100644
index 0000000..ab58ff5
--- /dev/null
+++ b/lib/nghttp3_vec.c
@@ -0,0 +1,55 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_vec.h"
+#include "nghttp3_macro.h"
+
+uint64_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) {
+ size_t i;
+ uint64_t res = 0;
+
+ for (i = 0; i < n; ++i) {
+ res += vec[i].len;
+ }
+
+ return res;
+}
+
+int64_t nghttp3_vec_len_varint(const nghttp3_vec *vec, size_t n) {
+ uint64_t res = 0;
+ size_t len;
+ size_t i;
+
+ for (i = 0; i < n; ++i) {
+ len = vec[i].len;
+ if (len > NGHTTP3_MAX_VARINT - res) {
+ return -1;
+ }
+
+ res += len;
+ }
+
+ return (int64_t)res;
+}
diff --git a/lib/nghttp3_vec.h b/lib/nghttp3_vec.h
new file mode 100644
index 0000000..473d146
--- /dev/null
+++ b/lib/nghttp3_vec.h
@@ -0,0 +1,41 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_VEC_H
+#define NGHTTP3_VEC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+/*
+ * nghttp3_vec_len_varint is similar to nghttp3_vec_len, but it
+ * returns -1 if the sum of the length exceeds NGHTTP3_MAX_VARINT.
+ */
+int64_t nghttp3_vec_len_varint(const nghttp3_vec *vec, size_t n);
+
+#endif /* NGHTTP3_VEC_H */
diff --git a/lib/nghttp3_version.c b/lib/nghttp3_version.c
new file mode 100644
index 0000000..c460cc7
--- /dev/null
+++ b/lib/nghttp3_version.c
@@ -0,0 +1,39 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+static nghttp3_info version = {NGHTTP3_VERSION_AGE, NGHTTP3_VERSION_NUM,
+ NGHTTP3_VERSION};
+
+const nghttp3_info *nghttp3_version(int least_version) {
+ if (least_version > NGHTTP3_VERSION_NUM) {
+ return NULL;
+ }
+ return &version;
+}