aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2019-12-09 14:57:59 +0100
committerLoïc Hoguin <[email protected]>2019-12-09 14:57:59 +0100
commit3b89df71753817636446925784dd92b69a4deb11 (patch)
tree126d91684d7779ec15e654874edb80237f673f9f
parent8aa74cb77a672b5b0ba528df77e8b9d0e53ee8a7 (diff)
downloadcowlib-3b89df71753817636446925784dd92b69a4deb11.tar.gz
cowlib-3b89df71753817636446925784dd92b69a4deb11.tar.bz2
cowlib-3b89df71753817636446925784dd92b69a4deb11.zip
WIP Optimize HPACK (de)compression speed
-rw-r--r--src/cow_hpack.erl627
1 files 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() ->