From 3b89df71753817636446925784dd92b69a4deb11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Mon, 9 Dec 2019 14:57:59 +0100 Subject: 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() -> -- cgit v1.2.3