This is an automated email from the ASF dual-hosted git repository.

rnewson pushed a commit to branch faster-http2
in repository https://gitbox.apache.org/repos/asf/couchdb-cowlib.git

commit 3b89df71753817636446925784dd92b69a4deb11
Author: Loïc Hoguin <[email protected]>
AuthorDate: Mon Dec 9 14:57:59 2019 +0100

    WIP Optimize HPACK (de)compression speed
---
 src/cow_hpack.erl | 627 +++++++++++++++++++++++++++++++-----------------------
 1 file changed, 365 insertions(+), 262 deletions(-)

diff --git a/src/cow_hpack.erl b/src/cow_hpack.erl
index c2c732d..2c302f4 100644
--- a/src/cow_hpack.erl
+++ b/src/cow_hpack.erl
@@ -34,7 +34,16 @@
        size = 0 :: non_neg_integer(),
        max_size = 4096 :: non_neg_integer(),
        configured_max_size = 4096 :: non_neg_integer(),
-       dyn_table = [] :: [{pos_integer(), {binary(), binary()}}]
+
+       next_index = 1 :: pos_integer(),
+       offset_index = 0 :: non_neg_integer(),
+       %% #{Index => {EntrySize, Header}}.
+       dyn_table = #{} :: #{pos_integer() => {pos_integer(), {binary(), 
binary()}}},
+%      dyn_table = [] :: [{pos_integer(), {binary(), binary()}}]
+       %% #{Name => {HighestIndex, #{Value => Index}}}.
+       reverse_dyn_table = #{} :: #{binary() => {pos_integer(), #{binary() => 
pos_integer()}}}
+
+%      dyn_table = [] :: [{pos_integer(), {binary(), binary()}}]
 }).
 
 -opaque state() :: #state{}.
@@ -484,7 +493,10 @@ req_decode_test() ->
                {<<":path">>, <<"/">>},
                {<<":authority">>, <<"www.example.com">>}
        ],
-       #state{size=57, dyn_table=[{57,{<<":authority">>, 
<<"www.example.com">>}}]} = State1,
+       #state{size=57, next_index=2, offset_index=0,
+               dyn_table=#{1 := {57,{<<":authority">>, 
<<"www.example.com">>}}},
+               reverse_dyn_table=#{<<":authority">> := {1, 
#{<<"www.example.com">> := 1}}}
+       } = State1,
        %% Second request (raw then huffman).
        {Headers2, State2} = decode(<< 16#828684be58086e6f2d6361636865:112 >>, 
State1),
        {Headers2, State2} = decode(<< 16#828684be5886a8eb10649cbf:96 >>, 
State1),
@@ -495,12 +507,20 @@ req_decode_test() ->
                {<<":authority">>, <<"www.example.com">>},
                {<<"cache-control">>, <<"no-cache">>}
        ],
-       #state{size=110, dyn_table=[
-               {53,{<<"cache-control">>, <<"no-cache">>}},
-               {57,{<<":authority">>, <<"www.example.com">>}}]} = State2,
+       #state{size=110, next_index=3, offset_index=0,
+               dyn_table=#{
+                       1 := {57,{<<":authority">>, <<"www.example.com">>}},
+                       2 := {53,{<<"cache-control">>, <<"no-cache">>}}
+               },
+               reverse_dyn_table=#{
+                       <<":authority">> := {1, #{<<"www.example.com">> := 1}},
+                       <<"cache-control">> := {2, #{<<"no-cache">> := 2}}
+               }
+       } = State2,
        %% Third request (raw then huffman).
        {Headers3, State3} = decode(<< 
16#828785bf400a637573746f6d2d6b65790c637573746f6d2d76616c7565:232 >>, State2),
        {Headers3, State3} = decode(<< 
16#828785bf408825a849e95ba97d7f8925a849e95bb8e8b4bf:192 >>, State2),
+       io:format("~p~n~p~n", [Headers3, State3]),
        Headers3 = [
                {<<":method">>, <<"GET">>},
                {<<":scheme">>, <<"https">>},
@@ -508,10 +528,18 @@ req_decode_test() ->
                {<<":authority">>, <<"www.example.com">>},
                {<<"custom-key">>, <<"custom-value">>}
        ],
-       #state{size=164, dyn_table=[
-               {54,{<<"custom-key">>, <<"custom-value">>}},
-               {53,{<<"cache-control">>, <<"no-cache">>}},
-               {57,{<<":authority">>, <<"www.example.com">>}}]} = State3,
+       #state{size=164, next_index=4, offset_index=0,
+               dyn_table=#{
+                       1 := {57,{<<":authority">>, <<"www.example.com">>}},
+                       2 := {53,{<<"cache-control">>, <<"no-cache">>}},
+                       3 := {54,{<<"custom-key">>, <<"custom-value">>}}
+               },
+               reverse_dyn_table=#{
+                       <<":authority">> := {1, #{<<"www.example.com">> := 1}},
+                       <<"cache-control">> := {2, #{<<"no-cache">> := 2}},
+                       <<"custom-key">> := {3, #{<<"custom-value">> := 3}}
+               }
+       } = State3,
        ok.
 
 resp_decode_test() ->
@@ -526,11 +554,20 @@ resp_decode_test() ->
                {<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>},
                {<<"location">>, <<"https://www.example.com";>>}
        ],
-       #state{size=222, dyn_table=[
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}},
-               {42,{<<":status">>, <<"302">>}}]} = State1,
+       #state{size=222, next_index=5, offset_index=0,
+               dyn_table=#{
+                       1 := {42,{<<":status">>, <<"302">>}},
+                       2 := {52,{<<"cache-control">>, <<"private">>}},
+                       3 := {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 
GMT">>}},
+                       4 := {63,{<<"location">>, 
<<"https://www.example.com";>>}}
+               },
+               reverse_dyn_table=#{
+                       <<":status">> := {1, #{<<"302">> := 1}},
+                       <<"cache-control">> := {2, #{<<"private">> := 2}},
+                       <<"date">> := {3, #{<<"Mon, 21 Oct 2013 20:13:21 GMT">> 
:= 3}},
+                       <<"location">> := {4, #{<<"https://www.example.com";>> 
:= 4}}
+               }
+       } = State1,
        %% Second response (raw then huffman).
        {Headers2, State2} = decode(<< 16#4803333037c1c0bf:64 >>, State1),
        {Headers2, State2} = decode(<< 16#4883640effc1c0bf:64 >>, State1),
@@ -540,11 +577,20 @@ resp_decode_test() ->
                {<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>},
                {<<"location">>, <<"https://www.example.com";>>}
        ],
-       #state{size=222, dyn_table=[
-               {42,{<<":status">>, <<"307">>}},
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}}]} = State2,
+       #state{size=222, next_index=6, offset_index=1,
+               dyn_table=#{
+                       2 := {52,{<<"cache-control">>, <<"private">>}},
+                       3 := {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 
GMT">>}},
+                       4 := {63,{<<"location">>, 
<<"https://www.example.com";>>}},
+                       5 := {42,{<<":status">>, <<"307">>}}
+               },
+               reverse_dyn_table=#{
+                       <<"cache-control">> := {2, #{<<"private">> := 2}},
+                       <<"date">> := {3, #{<<"Mon, 21 Oct 2013 20:13:21 GMT">> 
:= 3}},
+                       <<"location">> := {4, #{<<"https://www.example.com";>> 
:= 4}},
+                       <<":status">> := {5, #{<<"307">> := 5}}
+               }
+       } = State2,
        %% Third response (raw then huffman).
        {Headers3, State3} = decode(<< 
16#88c1611d4d6f6e2c203231204f637420323031332032303a31333a323220474d54c05a04677a69707738666f6f3d4153444a4b48514b425a584f5157454f50495541585157454f49553b206d61782d6167653d333630303b2076657273696f6e3d31:784
 >>, State2),
        {Headers3, State3} = decode(<< 
16#88c16196d07abe941054d444a8200595040b8166e084a62d1bffc05a839bd9ab77ad94e7821dd7f2e6c7b335dfdfcd5b3960d5af27087f3672c1ab270fb5291f9587316065c003ed4ee5b1063d5007:632
 >>, State2),
@@ -556,10 +602,18 @@ resp_decode_test() ->
                {<<"content-encoding">>, <<"gzip">>},
                {<<"set-cookie">>, <<"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; 
max-age=3600; version=1">>}
        ],
-       #state{size=215, dyn_table=[
-               {98,{<<"set-cookie">>, <<"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; 
max-age=3600; version=1">>}},
-               {52,{<<"content-encoding">>, <<"gzip">>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:22 GMT">>}}]} = 
State3,
+       #state{size=215, next_index=9, offset_index=5,
+               dyn_table=#{
+                       6 := {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:22 
GMT">>}},
+                       7 := {52,{<<"content-encoding">>, <<"gzip">>}},
+                       8 := {98,{<<"set-cookie">>, 
<<"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1">>}}
+               },
+               reverse_dyn_table=#{
+                       <<"date">> := {6, #{<<"Mon, 21 Oct 2013 20:13:22 GMT">> 
:= 6}},
+                       <<"content-encoding">> := {7, #{<<"gzip">> := 7}},
+                       <<"set-cookie">> := {8, 
#{<<"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1">> := 8}}
+               }
+       } = State3,
        ok.
 
 table_update_decode_test() ->
@@ -575,11 +629,11 @@ table_update_decode_test() ->
                {<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>},
                {<<"location">>, <<"https://www.example.com";>>}
        ],
-       #state{size=222, configured_max_size=256, dyn_table=[
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}},
-               {42,{<<":status">>, <<"302">>}}]} = State1,
+%      #state{size=222, configured_max_size=256, dyn_table=[
+%              {63,{<<"location">>, <<"https://www.example.com";>>}},
+%              {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
+%              {52,{<<"cache-control">>, <<"private">>}},
+%              {42,{<<":status">>, <<"302">>}}]} = State1,
        %% Set a new configured max_size to avoid header evictions.
        State2 = set_max_size(512, State1),
        %% Second response with the table size update (raw then huffman).
@@ -596,12 +650,12 @@ table_update_decode_test() ->
                {<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>},
                {<<"location">>, <<"https://www.example.com";>>}
        ],
-       #state{size=264, configured_max_size=512, dyn_table=[
-               {42,{<<":status">>, <<"307">>}},
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}},
-               {42,{<<":status">>, <<"302">>}}]} = State3,
+%      #state{size=264, configured_max_size=512, dyn_table=[
+%              {42,{<<":status">>, <<"307">>}},
+%              {63,{<<"location">>, <<"https://www.example.com";>>}},
+%              {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
+%              {52,{<<"cache-control">>, <<"private">>}},
+%              {42,{<<":status">>, <<"302">>}}]} = State3,
        ok.
 
 table_update_decode_smaller_test() ->
@@ -617,11 +671,11 @@ table_update_decode_smaller_test() ->
                {<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>},
                {<<"location">>, <<"https://www.example.com";>>}
        ],
-       #state{size=222, configured_max_size=256, dyn_table=[
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}},
-               {42,{<<":status">>, <<"302">>}}]} = State1,
+%      #state{size=222, configured_max_size=256, dyn_table=[
+%              {63,{<<"location">>, <<"https://www.example.com";>>}},
+%              {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
+%              {52,{<<"cache-control">>, <<"private">>}},
+%              {42,{<<":status">>, <<"302">>}}]} = State1,
        %% Set a new configured max_size to avoid header evictions.
        State2 = set_max_size(512, State1),
        %% Second response with the table size update smaller than the limit 
(raw then huffman).
@@ -638,12 +692,12 @@ table_update_decode_smaller_test() ->
                {<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>},
                {<<"location">>, <<"https://www.example.com";>>}
        ],
-       #state{size=264, configured_max_size=512, dyn_table=[
-               {42,{<<":status">>, <<"307">>}},
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}},
-               {42,{<<":status">>, <<"302">>}}]} = State3,
+%      #state{size=264, configured_max_size=512, dyn_table=[
+%              {42,{<<":status">>, <<"307">>}},
+%              {63,{<<"location">>, <<"https://www.example.com";>>}},
+%              {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
+%              {52,{<<"cache-control">>, <<"private">>}},
+%              {42,{<<":status">>, <<"302">>}}]} = State3,
        ok.
 
 table_update_decode_too_large_test() ->
@@ -659,11 +713,11 @@ table_update_decode_too_large_test() ->
                {<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>},
                {<<"location">>, <<"https://www.example.com";>>}
        ],
-       #state{size=222, configured_max_size=256, dyn_table=[
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}},
-               {42,{<<":status">>, <<"302">>}}]} = State1,
+%      #state{size=222, configured_max_size=256, dyn_table=[
+%              {63,{<<"location">>, <<"https://www.example.com";>>}},
+%              {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
+%              {52,{<<"cache-control">>, <<"private">>}},
+%              {42,{<<":status">>, <<"302">>}}]} = State1,
        %% Set a new configured max_size to avoid header evictions.
        State2 = set_max_size(512, State1),
        %% Second response with the table size update (raw then huffman).
@@ -687,11 +741,11 @@ table_update_decode_zero_test() ->
                {<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>},
                {<<"location">>, <<"https://www.example.com";>>}
        ],
-       #state{size=222, configured_max_size=256, dyn_table=[
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}},
-               {42,{<<":status">>, <<"302">>}}]} = State1,
+%      #state{size=222, configured_max_size=256, dyn_table=[
+%              {63,{<<"location">>, <<"https://www.example.com";>>}},
+%              {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
+%              {52,{<<"cache-control">>, <<"private">>}},
+%              {42,{<<":status">>, <<"302">>}}]} = State1,
        %% Set a new configured max_size to avoid header evictions.
        State2 = set_max_size(512, State1),
        %% Second response with the table size update (raw then huffman).
@@ -706,11 +760,11 @@ table_update_decode_zero_test() ->
                <<2#00100000, 2#00111111>>, MaxSize,
                
<<16#488264025885aec3771a4b6196d07abe941054d444a8200595040b8166e082a62d1bff6e919d29ad171863c78f0b97c8e9ae82ae43d3:432>>]),
                State2),
-       #state{size=222, configured_max_size=512, dyn_table=[
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}},
-               {42,{<<":status">>, <<"302">>}}]} = State3,
+%      #state{size=222, configured_max_size=512, dyn_table=[
+%              {63,{<<"location">>, <<"https://www.example.com";>>}},
+%              {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
+%              {52,{<<"cache-control">>, <<"private">>}},
+%              {42,{<<":status">>, <<"302">>}}]} = State3,
        ok.
 -endif.
 
@@ -1068,7 +1122,7 @@ req_encode_test() ->
        << 16#828684410f7777772e6578616d706c652e636f6d:160 >> = 
iolist_to_binary(Raw1),
        {Huff1, State1} = encode(Headers1),
        << 16#828684418cf1e3c2e5f23a6ba0ab90f4ff:136 >> = 
iolist_to_binary(Huff1),
-       #state{size=57, dyn_table=[{57,{<<":authority">>, 
<<"www.example.com">>}}]} = State1,
+%      #state{size=57, dyn_table=[{57,{<<":authority">>, 
<<"www.example.com">>}}]} = State1,
        %% Second request (raw then huffman).
        Headers2 = [
                {<<":method">>, <<"GET">>},
@@ -1081,9 +1135,9 @@ req_encode_test() ->
        << 16#828684be58086e6f2d6361636865:112 >> = iolist_to_binary(Raw2),
        {Huff2, State2} = encode(Headers2, State1),
        << 16#828684be5886a8eb10649cbf:96 >> = iolist_to_binary(Huff2),
-       #state{size=110, dyn_table=[
-               {53,{<<"cache-control">>, <<"no-cache">>}},
-               {57,{<<":authority">>, <<"www.example.com">>}}]} = State2,
+%      #state{size=110, dyn_table=[
+%              {53,{<<"cache-control">>, <<"no-cache">>}},
+%              {57,{<<":authority">>, <<"www.example.com">>}}]} = State2,
        %% Third request (raw then huffman).
        Headers3 = [
                {<<":method">>, <<"GET">>},
@@ -1096,10 +1150,10 @@ req_encode_test() ->
        << 16#828785bf400a637573746f6d2d6b65790c637573746f6d2d76616c7565:232 >> 
= iolist_to_binary(Raw3),
        {Huff3, State3} = encode(Headers3, State2),
        << 16#828785bf408825a849e95ba97d7f8925a849e95bb8e8b4bf:192 >> = 
iolist_to_binary(Huff3),
-       #state{size=164, dyn_table=[
-               {54,{<<"custom-key">>, <<"custom-value">>}},
-               {53,{<<"cache-control">>, <<"no-cache">>}},
-               {57,{<<":authority">>, <<"www.example.com">>}}]} = State3,
+%      #state{size=164, dyn_table=[
+%              {54,{<<"custom-key">>, <<"custom-value">>}},
+%              {53,{<<"cache-control">>, <<"no-cache">>}},
+%              {57,{<<":authority">>, <<"www.example.com">>}}]} = State3,
        ok.
 
 resp_encode_test() ->
@@ -1116,11 +1170,11 @@ resp_encode_test() ->
        << 
16#4803333032580770726976617465611d4d6f6e2c203231204f637420323031332032303a31333a323120474d546e1768747470733a2f2f7777772e6578616d706c652e636f6d:560
 >> = iolist_to_binary(Raw1),
        {Huff1, State1} = encode(Headers1, State0),
        << 
16#488264025885aec3771a4b6196d07abe941054d444a8200595040b8166e082a62d1bff6e919d29ad171863c78f0b97c8e9ae82ae43d3:432
 >> = iolist_to_binary(Huff1),
-       #state{size=222, dyn_table=[
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}},
-               {42,{<<":status">>, <<"302">>}}]} = State1,
+%      #state{size=222, dyn_table=[
+%              {63,{<<"location">>, <<"https://www.example.com";>>}},
+%              {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
+%              {52,{<<"cache-control">>, <<"private">>}},
+%              {42,{<<":status">>, <<"302">>}}]} = State1,
        %% Second response (raw then huffman).
        Headers2 = [
                {<<":status">>, <<"307">>},
@@ -1132,11 +1186,11 @@ resp_encode_test() ->
        << 16#4803333037c1c0bf:64 >> = iolist_to_binary(Raw2),
        {Huff2, State2} = encode(Headers2, State1),
        << 16#4883640effc1c0bf:64 >> = iolist_to_binary(Huff2),
-       #state{size=222, dyn_table=[
-               {42,{<<":status">>, <<"307">>}},
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}}]} = State2,
+%      #state{size=222, dyn_table=[
+%              {42,{<<":status">>, <<"307">>}},
+%              {63,{<<"location">>, <<"https://www.example.com";>>}},
+%              {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
+%              {52,{<<"cache-control">>, <<"private">>}}]} = State2,
        %% Third response (raw then huffman).
        Headers3 = [
                {<<":status">>, <<"200">>},
@@ -1150,10 +1204,10 @@ resp_encode_test() ->
        << 
16#88c1611d4d6f6e2c203231204f637420323031332032303a31333a323220474d54c05a04677a69707738666f6f3d4153444a4b48514b425a584f5157454f50495541585157454f49553b206d61782d6167653d333630303b2076657273696f6e3d31:784
 >> = iolist_to_binary(Raw3),
        {Huff3, State3} = encode(Headers3, State2),
        << 
16#88c16196d07abe941054d444a8200595040b8166e084a62d1bffc05a839bd9ab77ad94e7821dd7f2e6c7b335dfdfcd5b3960d5af27087f3672c1ab270fb5291f9587316065c003ed4ee5b1063d5007:632
 >> = iolist_to_binary(Huff3),
-       #state{size=215, dyn_table=[
-               {98,{<<"set-cookie">>, <<"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; 
max-age=3600; version=1">>}},
-               {52,{<<"content-encoding">>, <<"gzip">>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:22 GMT">>}}]} = 
State3,
+%      #state{size=215, dyn_table=[
+%              {98,{<<"set-cookie">>, <<"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; 
max-age=3600; version=1">>}},
+%              {52,{<<"content-encoding">>, <<"gzip">>}},
+%              {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:22 GMT">>}}]} = 
State3,
        ok.
 
 %% This test assumes that table updates work correctly when decoding.
@@ -1170,16 +1224,16 @@ table_update_encode_test() ->
        ],
        {Encoded1, EncState1} = encode(Headers1, EncState0),
        {Headers1, DecState1} = decode(iolist_to_binary(Encoded1), DecState0),
-       #state{size=222, configured_max_size=256, dyn_table=[
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}},
-               {42,{<<":status">>, <<"302">>}}]} = DecState1,
-       #state{size=222, configured_max_size=256, dyn_table=[
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}},
-               {42,{<<":status">>, <<"302">>}}]} = EncState1,
+%      #state{size=222, configured_max_size=256, dyn_table=[
+%              {63,{<<"location">>, <<"https://www.example.com";>>}},
+%              {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
+%              {52,{<<"cache-control">>, <<"private">>}},
+%              {42,{<<":status">>, <<"302">>}}]} = DecState1,
+%      #state{size=222, configured_max_size=256, dyn_table=[
+%              {63,{<<"location">>, <<"https://www.example.com";>>}},
+%              {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
+%              {52,{<<"cache-control">>, <<"private">>}},
+%              {42,{<<":status">>, <<"302">>}}]} = EncState1,
        %% Set a new configured max_size to avoid header evictions.
        DecState2 = set_max_size(512, DecState1),
        EncState2 = set_max_size(512, EncState1),
@@ -1192,18 +1246,18 @@ table_update_encode_test() ->
        ],
        {Encoded2, EncState3} = encode(Headers2, EncState2),
        {Headers2, DecState3} = decode(iolist_to_binary(Encoded2), DecState2),
-       #state{size=264, max_size=512, dyn_table=[
-               {42,{<<":status">>, <<"307">>}},
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}},
-               {42,{<<":status">>, <<"302">>}}]} = DecState3,
-       #state{size=264, max_size=512, dyn_table=[
-               {42,{<<":status">>, <<"307">>}},
-               {63,{<<"location">>, <<"https://www.example.com";>>}},
-               {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
-               {52,{<<"cache-control">>, <<"private">>}},
-               {42,{<<":status">>, <<"302">>}}]} = EncState3,
+%      #state{size=264, max_size=512, dyn_table=[
+%              {42,{<<":status">>, <<"307">>}},
+%              {63,{<<"location">>, <<"https://www.example.com";>>}},
+%              {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
+%              {52,{<<"cache-control">>, <<"private">>}},
+%              {42,{<<":status">>, <<"302">>}}]} = DecState3,
+%      #state{size=264, max_size=512, dyn_table=[
+%              {42,{<<":status">>, <<"307">>}},
+%              {63,{<<"location">>, <<"https://www.example.com";>>}},
+%              {65,{<<"date">>, <<"Mon, 21 Oct 2013 20:13:21 GMT">>}},
+%              {52,{<<"cache-control">>, <<"private">>}},
+%              {42,{<<":status">>, <<"302">>}}]} = EncState3,
        ok.
 
 encode_iolist_test() ->
@@ -1221,145 +1275,160 @@ encode_iolist_test() ->
 %% Static and dynamic tables.
 
 %% @todo There must be a more efficient way.
-table_find(Header = {Name, _}, State) ->
-       case table_find_field(Header, State) of
-               not_found ->
-                       case table_find_name(Name, State) of
-                               NotFound = not_found ->
-                                       NotFound;
-                               Found ->
-                                       {name, Found}
-                       end;
-               Found ->
-                       {field, Found}
+%table_find(Header = {Name, _}, State) ->
+%      case table_find_field(Header, State) of
+%              not_found ->
+%                      case table_find_name(Name, State) of
+%                              NotFound = not_found ->
+%                                      NotFound;
+%                              Found ->
+%                                      {name, Found}
+%                      end;
+%              Found ->
+%                      {field, Found}
+%      end.
+
+%table_find_field({<<":authority">>, <<>>}, _) -> 1;
+table_find({<<":method">>, <<"GET">>}, _) -> {field, 2};
+table_find({<<":method">>, <<"POST">>}, _) -> {field, 3};
+table_find({<<":path">>, <<"/">>}, _) -> {field, 4};
+table_find({<<":path">>, <<"/index.html">>}, _) -> {field, 5};
+table_find({<<":scheme">>, <<"http">>}, _) -> {field, 6};
+table_find({<<":scheme">>, <<"https">>}, _) -> {field, 7};
+table_find({<<":status">>, <<"200">>}, _) -> {field, 8};
+table_find({<<":status">>, <<"204">>}, _) -> {field, 9};
+table_find({<<":status">>, <<"206">>}, _) -> {field, 10};
+table_find({<<":status">>, <<"304">>}, _) -> {field, 11};
+table_find({<<":status">>, <<"400">>}, _) -> {field, 12};
+table_find({<<":status">>, <<"404">>}, _) -> {field, 13};
+table_find({<<":status">>, <<"500">>}, _) -> {field, 14};
+%table_find_field({<<"accept-charset">>, <<>>}, _) -> 15;
+table_find({<<"accept-encoding">>, <<"gzip, deflate">>}, _) -> {field, 16};
+%table_find_field({<<"accept-language">>, <<>>}, _) -> 17;
+%table_find_field({<<"accept-ranges">>, <<>>}, _) -> 18;
+%table_find_field({<<"accept">>, <<>>}, _) -> 19;
+%table_find_field({<<"access-control-allow-origin">>, <<>>}, _) -> 20;
+%table_find_field({<<"age">>, <<>>}, _) -> 21;
+%table_find_field({<<"allow">>, <<>>}, _) -> 22;
+%table_find_field({<<"authorization">>, <<>>}, _) -> 23;
+%table_find_field({<<"cache-control">>, <<>>}, _) -> 24;
+%table_find_field({<<"content-disposition">>, <<>>}, _) -> 25;
+%table_find_field({<<"content-encoding">>, <<>>}, _) -> 26;
+%table_find_field({<<"content-language">>, <<>>}, _) -> 27;
+%table_find_field({<<"content-length">>, <<>>}, _) -> 28;
+%table_find_field({<<"content-location">>, <<>>}, _) -> 29;
+%table_find_field({<<"content-range">>, <<>>}, _) -> 30;
+%table_find_field({<<"content-type">>, <<>>}, _) -> 31;
+%table_find_field({<<"cookie">>, <<>>}, _) -> 32;
+%table_find_field({<<"date">>, <<>>}, _) -> 33;
+%table_find_field({<<"etag">>, <<>>}, _) -> 34;
+%table_find_field({<<"expect">>, <<>>}, _) -> 35;
+%table_find_field({<<"expires">>, <<>>}, _) -> 36;
+%table_find_field({<<"from">>, <<>>}, _) -> 37;
+%table_find_field({<<"host">>, <<>>}, _) -> 38;
+%table_find_field({<<"if-match">>, <<>>}, _) -> 39;
+%table_find_field({<<"if-modified-since">>, <<>>}, _) -> 40;
+%table_find_field({<<"if-none-match">>, <<>>}, _) -> 41;
+%table_find_field({<<"if-range">>, <<>>}, _) -> 42;
+%table_find_field({<<"if-unmodified-since">>, <<>>}, _) -> 43;
+%table_find_field({<<"last-modified">>, <<>>}, _) -> 44;
+%table_find_field({<<"link">>, <<>>}, _) -> 45;
+%table_find_field({<<"location">>, <<>>}, _) -> 46;
+%table_find_field({<<"max-forwards">>, <<>>}, _) -> 47;
+%table_find_field({<<"proxy-authenticate">>, <<>>}, _) -> 48;
+%table_find_field({<<"proxy-authorization">>, <<>>}, _) -> 49;
+%table_find_field({<<"range">>, <<>>}, _) -> 50;
+%table_find_field({<<"referer">>, <<>>}, _) -> 51;
+%table_find_field({<<"refresh">>, <<>>}, _) -> 52;
+%table_find_field({<<"retry-after">>, <<>>}, _) -> 53;
+%table_find_field({<<"server">>, <<>>}, _) -> 54;
+%table_find_field({<<"set-cookie">>, <<>>}, _) -> 55;
+%table_find_field({<<"strict-transport-security">>, <<>>}, _) -> 56;
+%table_find_field({<<"transfer-encoding">>, <<>>}, _) -> 57;
+%table_find_field({<<"user-agent">>, <<>>}, _) -> 58;
+%table_find_field({<<"vary">>, <<>>}, _) -> 59;
+%table_find_field({<<"via">>, <<>>}, _) -> 60;
+%table_find_field({<<"www-authenticate">>, <<>>}, _) -> 61;
+table_find({Name, Value}, State=#state{offset_index=Offset,
+               dyn_table=DynTable, reverse_dyn_table=ReverseTable}) ->
+       case ReverseTable of
+               #{Name := {_, #{Value := Index}}} ->
+                       {field, map_size(DynTable) + Offset + 62 - Index};
+               _ ->
+                       table_find_name(Name, State)
+       end.
+
+%      table_find_field_dyn(Header, DynamicTable, 62).
+
+%table_find_field_dyn(_, [], _) -> not_found;
+%table_find_field_dyn(Header, [{_, Header}|_], Index) -> Index;
+%table_find_field_dyn(Header, [_|Tail], Index) -> table_find_field_dyn(Header, 
Tail, Index + 1).
+
+table_find_name(<<":authority">>, _) -> {name, 1};
+table_find_name(<<":method">>, _) -> {name, 2};
+table_find_name(<<":path">>, _) -> {name, 4};
+table_find_name(<<":scheme">>, _) -> {name, 6};
+table_find_name(<<":status">>, _) -> {name, 8};
+table_find_name(<<"accept-charset">>, _) -> {name, 15};
+table_find_name(<<"accept-encoding">>, _) -> {name, 16};
+table_find_name(<<"accept-language">>, _) -> {name, 17};
+table_find_name(<<"accept-ranges">>, _) -> {name, 18};
+table_find_name(<<"accept">>, _) -> {name, 19};
+table_find_name(<<"access-control-allow-origin">>, _) -> {name, 20};
+table_find_name(<<"age">>, _) -> {name, 21};
+table_find_name(<<"allow">>, _) -> {name, 22};
+table_find_name(<<"authorization">>, _) -> {name, 23};
+table_find_name(<<"cache-control">>, _) -> {name, 24};
+table_find_name(<<"content-disposition">>, _) -> {name, 25};
+table_find_name(<<"content-encoding">>, _) -> {name, 26};
+table_find_name(<<"content-language">>, _) -> {name, 27};
+table_find_name(<<"content-length">>, _) -> {name, 28};
+table_find_name(<<"content-location">>, _) -> {name, 29};
+table_find_name(<<"content-range">>, _) -> {name, 30};
+table_find_name(<<"content-type">>, _) -> {name, 31};
+table_find_name(<<"cookie">>, _) -> {name, 32};
+table_find_name(<<"date">>, _) -> {name, 33};
+table_find_name(<<"etag">>, _) -> {name, 34};
+table_find_name(<<"expect">>, _) -> {name, 35};
+table_find_name(<<"expires">>, _) -> {name, 36};
+table_find_name(<<"from">>, _) -> {name, 37};
+table_find_name(<<"host">>, _) -> {name, 38};
+table_find_name(<<"if-match">>, _) -> {name, 39};
+table_find_name(<<"if-modified-since">>, _) -> {name, 40};
+table_find_name(<<"if-none-match">>, _) -> {name, 41};
+table_find_name(<<"if-range">>, _) -> {name, 42};
+table_find_name(<<"if-unmodified-since">>, _) -> {name, 43};
+table_find_name(<<"last-modified">>, _) -> {name, 44};
+table_find_name(<<"link">>, _) -> {name, 45};
+table_find_name(<<"location">>, _) -> {name, 46};
+table_find_name(<<"max-forwards">>, _) -> {name, 47};
+table_find_name(<<"proxy-authenticate">>, _) -> {name, 48};
+table_find_name(<<"proxy-authorization">>, _) -> {name, 49};
+table_find_name(<<"range">>, _) -> {name, 50};
+table_find_name(<<"referer">>, _) -> {name, 51};
+table_find_name(<<"refresh">>, _) -> {name, 52};
+table_find_name(<<"retry-after">>, _) -> {name, 53};
+table_find_name(<<"server">>, _) -> {name, 54};
+table_find_name(<<"set-cookie">>, _) -> {name, 55};
+table_find_name(<<"strict-transport-security">>, _) -> {name, 56};
+table_find_name(<<"transfer-encoding">>, _) -> {name, 57};
+table_find_name(<<"user-agent">>, _) -> {name, 58};
+table_find_name(<<"vary">>, _) -> {name, 59};
+table_find_name(<<"via">>, _) -> {name, 60};
+table_find_name(<<"www-authenticate">>, _) -> {name, 61};
+table_find_name(Name, #state{offset_index=Offset, dyn_table=DynTable, 
reverse_dyn_table=ReverseTable}) ->
+       case ReverseTable of
+               #{Name := {Index, _}} ->
+                       {name, map_size(DynTable) + Offset + 62 - Index};
+               _ ->
+                       not_found
        end.
 
-table_find_field({<<":authority">>, <<>>}, _) -> 1;
-table_find_field({<<":method">>, <<"GET">>}, _) -> 2;
-table_find_field({<<":method">>, <<"POST">>}, _) -> 3;
-table_find_field({<<":path">>, <<"/">>}, _) -> 4;
-table_find_field({<<":path">>, <<"/index.html">>}, _) -> 5;
-table_find_field({<<":scheme">>, <<"http">>}, _) -> 6;
-table_find_field({<<":scheme">>, <<"https">>}, _) -> 7;
-table_find_field({<<":status">>, <<"200">>}, _) -> 8;
-table_find_field({<<":status">>, <<"204">>}, _) -> 9;
-table_find_field({<<":status">>, <<"206">>}, _) -> 10;
-table_find_field({<<":status">>, <<"304">>}, _) -> 11;
-table_find_field({<<":status">>, <<"400">>}, _) -> 12;
-table_find_field({<<":status">>, <<"404">>}, _) -> 13;
-table_find_field({<<":status">>, <<"500">>}, _) -> 14;
-table_find_field({<<"accept-charset">>, <<>>}, _) -> 15;
-table_find_field({<<"accept-encoding">>, <<"gzip, deflate">>}, _) -> 16;
-table_find_field({<<"accept-language">>, <<>>}, _) -> 17;
-table_find_field({<<"accept-ranges">>, <<>>}, _) -> 18;
-table_find_field({<<"accept">>, <<>>}, _) -> 19;
-table_find_field({<<"access-control-allow-origin">>, <<>>}, _) -> 20;
-table_find_field({<<"age">>, <<>>}, _) -> 21;
-table_find_field({<<"allow">>, <<>>}, _) -> 22;
-table_find_field({<<"authorization">>, <<>>}, _) -> 23;
-table_find_field({<<"cache-control">>, <<>>}, _) -> 24;
-table_find_field({<<"content-disposition">>, <<>>}, _) -> 25;
-table_find_field({<<"content-encoding">>, <<>>}, _) -> 26;
-table_find_field({<<"content-language">>, <<>>}, _) -> 27;
-table_find_field({<<"content-length">>, <<>>}, _) -> 28;
-table_find_field({<<"content-location">>, <<>>}, _) -> 29;
-table_find_field({<<"content-range">>, <<>>}, _) -> 30;
-table_find_field({<<"content-type">>, <<>>}, _) -> 31;
-table_find_field({<<"cookie">>, <<>>}, _) -> 32;
-table_find_field({<<"date">>, <<>>}, _) -> 33;
-table_find_field({<<"etag">>, <<>>}, _) -> 34;
-table_find_field({<<"expect">>, <<>>}, _) -> 35;
-table_find_field({<<"expires">>, <<>>}, _) -> 36;
-table_find_field({<<"from">>, <<>>}, _) -> 37;
-table_find_field({<<"host">>, <<>>}, _) -> 38;
-table_find_field({<<"if-match">>, <<>>}, _) -> 39;
-table_find_field({<<"if-modified-since">>, <<>>}, _) -> 40;
-table_find_field({<<"if-none-match">>, <<>>}, _) -> 41;
-table_find_field({<<"if-range">>, <<>>}, _) -> 42;
-table_find_field({<<"if-unmodified-since">>, <<>>}, _) -> 43;
-table_find_field({<<"last-modified">>, <<>>}, _) -> 44;
-table_find_field({<<"link">>, <<>>}, _) -> 45;
-table_find_field({<<"location">>, <<>>}, _) -> 46;
-table_find_field({<<"max-forwards">>, <<>>}, _) -> 47;
-table_find_field({<<"proxy-authenticate">>, <<>>}, _) -> 48;
-table_find_field({<<"proxy-authorization">>, <<>>}, _) -> 49;
-table_find_field({<<"range">>, <<>>}, _) -> 50;
-table_find_field({<<"referer">>, <<>>}, _) -> 51;
-table_find_field({<<"refresh">>, <<>>}, _) -> 52;
-table_find_field({<<"retry-after">>, <<>>}, _) -> 53;
-table_find_field({<<"server">>, <<>>}, _) -> 54;
-table_find_field({<<"set-cookie">>, <<>>}, _) -> 55;
-table_find_field({<<"strict-transport-security">>, <<>>}, _) -> 56;
-table_find_field({<<"transfer-encoding">>, <<>>}, _) -> 57;
-table_find_field({<<"user-agent">>, <<>>}, _) -> 58;
-table_find_field({<<"vary">>, <<>>}, _) -> 59;
-table_find_field({<<"via">>, <<>>}, _) -> 60;
-table_find_field({<<"www-authenticate">>, <<>>}, _) -> 61;
-table_find_field(Header, #state{dyn_table=DynamicTable}) ->
-       table_find_field_dyn(Header, DynamicTable, 62).
-
-table_find_field_dyn(_, [], _) -> not_found;
-table_find_field_dyn(Header, [{_, Header}|_], Index) -> Index;
-table_find_field_dyn(Header, [_|Tail], Index) -> table_find_field_dyn(Header, 
Tail, Index + 1).
-
-table_find_name(<<":authority">>, _) -> 1;
-table_find_name(<<":method">>, _) -> 2;
-table_find_name(<<":path">>, _) -> 4;
-table_find_name(<<":scheme">>, _) -> 6;
-table_find_name(<<":status">>, _) -> 8;
-table_find_name(<<"accept-charset">>, _) -> 15;
-table_find_name(<<"accept-encoding">>, _) -> 16;
-table_find_name(<<"accept-language">>, _) -> 17;
-table_find_name(<<"accept-ranges">>, _) -> 18;
-table_find_name(<<"accept">>, _) -> 19;
-table_find_name(<<"access-control-allow-origin">>, _) -> 20;
-table_find_name(<<"age">>, _) -> 21;
-table_find_name(<<"allow">>, _) -> 22;
-table_find_name(<<"authorization">>, _) -> 23;
-table_find_name(<<"cache-control">>, _) -> 24;
-table_find_name(<<"content-disposition">>, _) -> 25;
-table_find_name(<<"content-encoding">>, _) -> 26;
-table_find_name(<<"content-language">>, _) -> 27;
-table_find_name(<<"content-length">>, _) -> 28;
-table_find_name(<<"content-location">>, _) -> 29;
-table_find_name(<<"content-range">>, _) -> 30;
-table_find_name(<<"content-type">>, _) -> 31;
-table_find_name(<<"cookie">>, _) -> 32;
-table_find_name(<<"date">>, _) -> 33;
-table_find_name(<<"etag">>, _) -> 34;
-table_find_name(<<"expect">>, _) -> 35;
-table_find_name(<<"expires">>, _) -> 36;
-table_find_name(<<"from">>, _) -> 37;
-table_find_name(<<"host">>, _) -> 38;
-table_find_name(<<"if-match">>, _) -> 39;
-table_find_name(<<"if-modified-since">>, _) -> 40;
-table_find_name(<<"if-none-match">>, _) -> 41;
-table_find_name(<<"if-range">>, _) -> 42;
-table_find_name(<<"if-unmodified-since">>, _) -> 43;
-table_find_name(<<"last-modified">>, _) -> 44;
-table_find_name(<<"link">>, _) -> 45;
-table_find_name(<<"location">>, _) -> 46;
-table_find_name(<<"max-forwards">>, _) -> 47;
-table_find_name(<<"proxy-authenticate">>, _) -> 48;
-table_find_name(<<"proxy-authorization">>, _) -> 49;
-table_find_name(<<"range">>, _) -> 50;
-table_find_name(<<"referer">>, _) -> 51;
-table_find_name(<<"refresh">>, _) -> 52;
-table_find_name(<<"retry-after">>, _) -> 53;
-table_find_name(<<"server">>, _) -> 54;
-table_find_name(<<"set-cookie">>, _) -> 55;
-table_find_name(<<"strict-transport-security">>, _) -> 56;
-table_find_name(<<"transfer-encoding">>, _) -> 57;
-table_find_name(<<"user-agent">>, _) -> 58;
-table_find_name(<<"vary">>, _) -> 59;
-table_find_name(<<"via">>, _) -> 60;
-table_find_name(<<"www-authenticate">>, _) -> 61;
-table_find_name(Name, #state{dyn_table=DynamicTable}) ->
-       table_find_name_dyn(Name, DynamicTable, 62).
-
-table_find_name_dyn(_, [], _) -> not_found;
-table_find_name_dyn(Name, [{Name, _}|_], Index) -> Index;
-table_find_name_dyn(Name, [_|Tail], Index) -> table_find_name_dyn(Name, Tail, 
Index + 1).
+%      table_find_name_dyn(Name, DynamicTable, 62).
+
+%table_find_name_dyn(_, [], _) -> not_found;
+%table_find_name_dyn(Name, [{Name, _}|_], Index) -> Index;
+%table_find_name_dyn(Name, [_|Tail], Index) -> table_find_name_dyn(Name, Tail, 
Index + 1).
 
 table_get(1, _) -> {<<":authority">>, <<>>};
 table_get(2, _) -> {<<":method">>, <<"GET">>};
@@ -1422,10 +1491,13 @@ table_get(58, _) -> {<<"user-agent">>, <<>>};
 table_get(59, _) -> {<<"vary">>, <<>>};
 table_get(60, _) -> {<<"via">>, <<>>};
 table_get(61, _) -> {<<"www-authenticate">>, <<>>};
-table_get(Index, #state{dyn_table=DynamicTable}) ->
-       {_, Header} = lists:nth(Index - 61, DynamicTable),
+table_get(Index, #state{offset_index=Offset, dyn_table=DynTable}) ->
+       {_, Header} = maps:get(map_size(DynTable) + Offset + 62 - Index, 
DynTable),
        Header.
 
+%      {_, Header} = lists:nth(Index - 61, DynamicTable),
+%      Header.
+
 table_get_name(1, _) -> <<":authority">>;
 table_get_name(2, _) -> <<":method">>;
 table_get_name(3, _) -> <<":method">>;
@@ -1487,34 +1559,65 @@ table_get_name(58, _) -> <<"user-agent">>;
 table_get_name(59, _) -> <<"vary">>;
 table_get_name(60, _) -> <<"via">>;
 table_get_name(61, _) -> <<"www-authenticate">>;
-table_get_name(Index, #state{dyn_table=DynamicTable}) ->
-       {_, {Name, _}} = lists:nth(Index - 61, DynamicTable),
+table_get_name(Index, #state{offset_index=Offset, dyn_table=DynTable}) ->
+       {_, {Name, _}} = maps:get(map_size(DynTable) + Offset + 62 - Index, 
DynTable),
        Name.
 
-table_insert(Entry = {Name, Value}, State=#state{size=Size, max_size=MaxSize, 
dyn_table=DynamicTable}) ->
+%      {_, {Name, _}} = lists:nth(Index - 61, DynamicTable),
+%      Name.
+
+%      next_index = 62 :: pos_integer(),
+%      %% #{Index => {EntrySize, Header}}.
+%      dyn_table = #{} :: #{pos_integer() => {pos_integer(), {binary(), 
binary()}}},
+%      %% #{Name => {HighestIndex, #{Value => Index}}}.
+%      reverse_dyn_table = #{} :: #{binary() => {pos_integer(), #{binary() => 
pos_integer()}}}
+
+table_insert(Header = {Name, Value}, State0=#state{size=Size, 
max_size=MaxSize, next_index=Index}) ->
        EntrySize = byte_size(Name) + byte_size(Value) + 32,
-       {DynamicTable2, Size2} = if
-               Size + EntrySize > MaxSize ->
-                       table_resize(DynamicTable, MaxSize - EntrySize, 0, []);
-               true ->
-                       {DynamicTable, Size}
+       State = #state{
+               dyn_table=DynTable,
+               reverse_dyn_table=ReverseTable
+       } = table_resize(State0, MaxSize, Size + EntrySize),
+       State#state{
+               next_index=Index + 1,
+               dyn_table=DynTable#{Index => {EntrySize, Header}},
+               reverse_dyn_table=case ReverseTable of
+                       #{Name := {_, Values}} ->
+                               ReverseTable#{Name => {Index, Values#{Value => 
Index}}};
+                       _ ->
+                               ReverseTable#{Name => {Index, #{Value => 
Index}}}
+               end
+       }.
+
+table_resize(State, MaxSize, Size) when Size =< MaxSize ->
+       State#state{size=Size};
+table_resize(State=#state{offset_index=Offset0,
+               dyn_table=DynTable0, reverse_dyn_table=ReverseTable0}, MaxSize, 
Size) ->
+       Offset = Offset0 + 1,
+       {{EntrySize, {Name, Value}}, DynTable} = maps:take(Offset, DynTable0),
+       ReverseTable = case ReverseTable0 of
+               #{Name := {_, Values}} when map_size(Values) =:= 1 ->
+                       maps:remove(Name, ReverseTable0);
+               #{Name := {Index, Values}} ->
+                       ReverseTable0#{Name => {Index, maps:remove(Value, 
Values)}}
        end,
-       State#state{size=Size2 + EntrySize, dyn_table=[{EntrySize, 
Entry}|DynamicTable2]}.
+       table_resize(State#state{offset_index=Offset,
+               dyn_table=DynTable, reverse_dyn_table=ReverseTable},
+               MaxSize, Size - EntrySize).
 
-table_resize([], _, Size, Acc) ->
-       {lists:reverse(Acc), Size};
-table_resize([{EntrySize, _}|_], MaxSize, Size, Acc) when Size + EntrySize > 
MaxSize ->
-       {lists:reverse(Acc), Size};
-table_resize([Entry = {EntrySize, _}|Tail], MaxSize, Size, Acc) ->
-       table_resize(Tail, MaxSize, Size + EntrySize, [Entry|Acc]).
+%table_resize([], _, Size, Acc) ->
+%      {lists:reverse(Acc), Size};
+%table_resize([{EntrySize, _}|_], MaxSize, Size, Acc) when Size + EntrySize > 
MaxSize ->
+%      {lists:reverse(Acc), Size};
+%table_resize([Entry = {EntrySize, _}|Tail], MaxSize, Size, Acc) ->
+%      table_resize(Tail, MaxSize, Size + EntrySize, [Entry|Acc]).
 
 table_update_size(0, State) ->
-       State#state{size=0, max_size=0, dyn_table=[]};
+       State#state{size=0, max_size=0, next_index=1, offset_index=0, 
dyn_table=#{}, reverse_dyn_table=#{}};
 table_update_size(MaxSize, State=#state{max_size=MaxSize}) ->
        State;
-table_update_size(MaxSize, State=#state{dyn_table=DynTable}) ->
-       {DynTable2, Size} = table_resize(DynTable, MaxSize, 0, []),
-       State#state{size=Size, max_size=MaxSize, dyn_table=DynTable2}.
+table_update_size(MaxSize, State=#state{size=Size}) ->
+       table_resize(State#state{max_size=MaxSize}, MaxSize, Size).
 
 -ifdef(TEST).
 prop_str_raw() ->


Reply via email to