diff --git a/NEWS.md b/NEWS.md index 665c261e..bb9b7230 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,20 @@ UNRELEASED ### Refactor +- HTTP/2 is now delegated to the `erlang_h2` library (hex `h2` 0.3.0). + Hackney no longer ships its own HTTP/2 framing, HPACK codec, or + connection/stream state machine: + - `hackney_http2.erl`, `hackney_http2_machine.erl`, `hackney_hpack.erl` + and the `hackney_hpack_huffman*` headers have been removed. + - `hackney_conn.erl` now starts an `h2_connection` gen_statem on the + post-ALPN socket, transfers socket ownership, and translates + `{h2, Conn, Event}` owner-messages into hackney's sync replies or + `{hackney_response, Ref, _}` async events. + - Server push handling (RFC 7540 §8.2, deprecated) is no longer exposed. + The `enable_push` option is a no-op. + - Public user-facing API is unchanged: `hackney:request/5`, streaming + async responses, pooled HTTP/2 connections, and `request_async` all + behave as before. - HTTP/3 is now delegated to the `erlang_quic` library's `quic_h3` module (hex `quic` 1.0.0). Hackney no longer ships its own HTTP/3 framing, QPACK codec, control-stream or unidirectional-stream handling: diff --git a/guides/http2_guide.md b/guides/http2_guide.md index ab7f7c99..c7be1e85 100644 --- a/guides/http2_guide.md +++ b/guides/http2_guide.md @@ -13,7 +13,8 @@ Hackney supports HTTP/2 with automatic protocol negotiation via ALPN (Applicatio - **Multiplexing** - Multiple requests share a single connection - **Header compression** - HPACK compression reduces overhead - **Flow control** - Automatic window management -- **Server push** - Optional support for server-initiated streams +- **Delegated to `erlang_h2`** - the underlying HTTP/2 stack is the `h2` + hex package; hackney exposes the same request API it always did ## Quick Start @@ -243,20 +244,10 @@ end) || Path <- Paths]. ## Server Push -Server push allows the server to send resources before the client requests them. By default, hackney rejects server pushes. To handle them: - -```erlang -%% Using low-level API with push handler -{ok, Conn} = hackney_conn:start_link(#{ - host => "example.com", - port => 443, - transport => hackney_ssl, - enable_push => self() %% Receive push notifications -}), - -%% Push notifications arrive as messages: -%% {hackney_push, ConnPid, {PromisedStreamId, Method, Scheme, Authority, Path, Headers}} -``` +Server push (RFC 7540 §8.2) is deprecated and no longer supported by the +underlying `h2` library. The `enable_push` option is accepted for +backwards-compatibility but is a no-op; pushes from the server are silently +refused. ## Flow Control @@ -275,11 +266,14 @@ HTTP/2 specific errors: case hackney:get(URL) of {ok, Status, Headers, Body} -> ok; - {error, {h2_connection_error, ErrorCode}} -> - %% HTTP/2 connection-level error - io:format("HTTP/2 error: ~p~n", [ErrorCode]); - {error, closed} -> - %% Connection closed (GOAWAY received) + {error, {goaway, ErrorCode}} -> + %% Peer sent GOAWAY + io:format("HTTP/2 GOAWAY: ~p~n", [ErrorCode]); + {error, {stream_error, ErrorCode}} -> + %% Peer sent RST_STREAM for this request + io:format("HTTP/2 stream reset: ~p~n", [ErrorCode]); + {error, {closed, _Reason}} -> + %% Connection closed ok; {error, Reason} -> io:format("Error: ~p~n", [Reason]) diff --git a/rebar.config b/rebar.config index 84d410da..d32bb642 100644 --- a/rebar.config +++ b/rebar.config @@ -59,6 +59,8 @@ {deps, [ %% Pure Erlang QUIC + HTTP/3 stack {quic, "1.0.0"}, + %% Pure Erlang HTTP/2 stack + {h2, "0.3.0"}, {idna, "~>7.1.0"}, {mimerl, "~>1.4"}, {certifi, "~>2.16.0"}, @@ -115,7 +117,7 @@ {exclude_mods, [hackney_metrics_prometheus]}, {base_plt_apps, [erts, stdlib, kernel, crypto, runtime_tools]}, {plt_apps, top_level_deps}, - {plt_extra_apps, [quic]}, + {plt_extra_apps, [quic, h2]}, {plt_location, local}, {plt_prefix, "hackney"}, {base_plt_location, "."}, diff --git a/src/hackney.app.src b/src/hackney.app.src index a553aa8d..1313a3b7 100644 --- a/src/hackney.app.src +++ b/src/hackney.app.src @@ -15,7 +15,8 @@ idna, mimerl, certifi, - ssl_verify_fun]}, + ssl_verify_fun, + h2]}, {included_applications, []}, {mod, { hackney_app, []}}, {env, [{timeout, 150000}, diff --git a/src/hackney_conn.erl b/src/hackney_conn.erl index 469783ce..cf7a7a49 100644 --- a/src/hackney_conn.erl +++ b/src/hackney_conn.erl @@ -169,13 +169,18 @@ %% HTTP/2 support %% Protocol negotiated via ALPN (http1, http2, or http3) protocol = http1 :: http1 | http2 | http3, - %% HTTP/2 connection state machine (from hackney_http2_machine) - h2_machine :: tuple() | undefined, + %% HTTP/2 connection pid (from h2 library) + h2_conn :: pid() | undefined, + %% Monitor ref for the h2_connection process so hackney_conn fails fast + %% if the h2 lib crashes. + h2_mon :: reference() | undefined, %% Map of active HTTP/2 streams: StreamId => {From, StreamState} - %% StreamState: waiting_headers | waiting_body | done | {push, Headers} - h2_streams = #{} :: #{pos_integer() => {gen_statem:from() | pid(), atom() | tuple()}}, - %% Server push handling: false = reject all (default), pid = send notifications to pid - enable_push = false :: false | pid(), + %% StreamState: + %% {sync, waiting_headers} + %% {sync, body, Status, Headers, AccBody} + %% {async, AsyncMode, StreamTo, Ref, waiting_headers} + %% {async, AsyncMode, StreamTo, Ref, streaming, Status, Headers} + h2_streams = #{} :: #{pos_integer() => {term(), tuple()}}, %% HTTP/3 support (QUIC) %% HTTP/3 connection reference from hackney_h3 @@ -507,7 +512,6 @@ init([DefaultOwner, Opts]) -> connect_options = maps:get(connect_options, Opts, []), ssl_options = maps:get(ssl_options, Opts, []), pool_pid = maps:get(pool_pid, Opts, undefined), - enable_push = maps:get(enable_push, Opts, false), no_reuse = maps:get(no_reuse, Opts, false), inform_fun = maps:get(inform_fun, Opts, undefined), auto_decompress = maps:get(auto_decompress, Opts, false) @@ -521,51 +525,32 @@ init([DefaultOwner, Opts]) -> {ok, connected, Data} end. -terminate(_Reason, _State, #conn_data{socket = Socket, transport = Transport, h2_machine = H2Machine, h3_conn = H3Conn}) -> - %% Cancel any HTTP/2 timers to prevent orphaned timer messages - cancel_h2_timers(H2Machine), +terminate(_Reason, _State, #conn_data{socket = Socket, transport = Transport, + h2_conn = H2Conn, h2_mon = H2Mon, + h3_conn = H3Conn}) -> + %% Close HTTP/2 connection (owns its socket) + case H2Conn of + undefined -> ok; + _ -> + case H2Mon of + undefined -> ok; + _ -> erlang:demonitor(H2Mon, [flush]) + end, + catch h2_connection:close(H2Conn) + end, %% Close HTTP/3 connection if present case H3Conn of undefined -> ok; _ -> hackney_h3:close(H3Conn) end, - %% Close socket - case Socket of - undefined -> ok; - _ -> Transport:close(Socket) + %% Close socket (only still owned by us for HTTP/1.1; h2 lib closes its own) + case {Socket, H2Conn} of + {undefined, _} -> ok; + {_, undefined} -> Transport:close(Socket); + _ -> ok end, ok. -%% @private Cancel HTTP/2 preface and settings timers -cancel_h2_timers(undefined) -> - ok; -cancel_h2_timers(H2Machine) when is_tuple(H2Machine), element(1, H2Machine) =:= http2_machine -> - %% H2Machine is #http2_machine{} record: - %% Element 1: http2_machine (record name) - %% Element 2: mode - %% Element 3: opts - %% Element 4: state - %% Element 5: preface_timer - %% Element 6: settings_timer - try - case element(5, H2Machine) of - undefined -> ok; - PrefaceTimer -> - _ = erlang:cancel_timer(PrefaceTimer, [{async, true}, {info, false}]), - ok - end, - case element(6, H2Machine) of - undefined -> ok; - SettingsTimer -> - _ = erlang:cancel_timer(SettingsTimer, [{async, true}, {info, false}]), - ok - end - catch - _:_ -> ok - end; -cancel_h2_timers(_) -> - ok. - code_change(_OldVsn, State, Data, _Extra) -> {ok, State, Data}. @@ -907,11 +892,12 @@ connected({call, From}, {send_headers, Method, Path, Headers}, Data) -> %% Transition to streaming_body state {next_state, streaming_body, NewData, [{next_event, internal, {send_headers_only, Method, Path, Headers}}]}; -%% HTTP/2 socket data handling -connected(info, {ssl, Socket, RecvData}, #conn_data{socket = Socket, protocol = http2} = Data) -> - handle_h2_data(RecvData, Data); -connected(info, {tcp, Socket, RecvData}, #conn_data{socket = Socket, protocol = http2} = Data) -> - handle_h2_data(RecvData, Data); +%% HTTP/2 owner messages from h2 library +connected(info, {h2, H2Conn, Event}, #conn_data{h2_conn = H2Conn} = Data) -> + handle_h2_event(Event, Data); +connected(info, {'DOWN', Mon, process, _Pid, Reason}, + #conn_data{h2_mon = Mon} = Data) -> + h2_on_closed(Reason, Data#conn_data{h2_conn = undefined, h2_mon = undefined}); connected(info, {tcp_closed, Socket}, #conn_data{socket = Socket} = Data) -> {next_state, closed, Data#conn_data{socket = undefined}}; @@ -925,13 +911,11 @@ connected(info, {tcp_error, Socket, _Reason}, #conn_data{socket = Socket} = Data connected(info, {ssl_error, Socket, _Reason}, #conn_data{socket = Socket} = Data) -> {next_state, closed, Data#conn_data{socket = undefined}}; -%% Unexpected data received while idle (HTTP/1.1 only - HTTP/2 handled above) -%% This could be trailing data from server or protocol violation - close connection -connected(info, {tcp, Socket, _UnexpectedData}, #conn_data{socket = Socket, protocol = Protocol} = Data) - when Protocol =/= http2 -> +%% Unexpected data received while idle - HTTP/1.1 only (H/2 socket is owned +%% by h2_connection; H/3 uses QUIC messages). +connected(info, {tcp, Socket, _UnexpectedData}, #conn_data{socket = Socket} = Data) -> {next_state, closed, Data#conn_data{socket = undefined}}; -connected(info, {ssl, Socket, _UnexpectedData}, #conn_data{socket = Socket, protocol = Protocol} = Data) - when Protocol =/= http2 -> +connected(info, {ssl, Socket, _UnexpectedData}, #conn_data{socket = Socket} = Data) -> {next_state, closed, Data#conn_data{socket = undefined}}; %% HTTP/3 message handling @@ -2292,262 +2276,133 @@ do_tcp_connect(From, Data) -> {stop_and_reply, normal, [{reply, From, {error, Reason}}]} end. -%% @private Initialize HTTP/2 connection -%% Sends the connection preface and initializes the h2_machine state +%% @private Initialize HTTP/2 connection via the h2 library. +%% The h2_connection process takes ownership of the socket and delivers +%% owner messages ({h2, Conn, Event}) to this gen_statem's mailbox. init_h2_connection(Socket, Data, From) -> - #conn_data{transport = Transport} = Data, - %% Initialize HTTP/2 state machine in client mode - {ok, Preface, H2Machine} = hackney_http2_machine:init(client, #{}), - %% Send HTTP/2 connection preface (includes SETTINGS frame) - case Transport:send(Socket, Preface) of - ok -> - %% Set socket to active mode for receiving server SETTINGS - case Transport:setopts(Socket, [{active, once}]) of - ok -> - NewData = Data#conn_data{ - h2_machine = H2Machine, - h2_streams = #{} - }, - {next_state, connected, NewData, [{reply, From, ok}]}; - {error, SetoptsErr} -> - {stop_and_reply, normal, [{reply, From, {error, SetoptsErr}}]} - end; - {error, Reason} -> - {stop_and_reply, normal, [{reply, From, {error, Reason}}]} - end. + start_h2_connection(Socket, Data, From, first_connect). %% @private Initialize HTTP/2 after SSL upgrade (e.g., after CONNECT proxy tunnel) -%% Similar to init_h2_connection but called from connected state init_h2_after_upgrade(SslSocket, Data, From) -> + start_h2_connection(SslSocket, Data, From, after_upgrade). + +start_h2_connection(Socket, Data, From, Origin) -> #conn_data{transport = Transport} = Data, - %% Initialize HTTP/2 state machine in client mode - {ok, Preface, H2Machine} = hackney_http2_machine:init(client, #{}), - %% Send HTTP/2 connection preface (includes SETTINGS frame) - case Transport:send(SslSocket, Preface) of - ok -> - %% Set socket to active mode for receiving server SETTINGS - case Transport:setopts(SslSocket, [{active, once}]) of + case h2_connection:start_link(client, Socket, self(), #{}) of + {ok, H2Conn} -> + %% Transfer socket ownership then activate handshake. + _ = Transport:controlling_process(Socket, H2Conn), + case h2_connection:activate(H2Conn) of ok -> - NewData = Data#conn_data{ - h2_machine = H2Machine, - h2_streams = #{} - }, - {keep_state, NewData, [{reply, From, ok}]}; - {error, SetoptsErr} -> - {keep_state_and_data, [{reply, From, {error, SetoptsErr}}]} + case h2_connection:wait_connected(H2Conn, + Data#conn_data.connect_timeout) of + ok -> + Mon = erlang:monitor(process, H2Conn), + NewData = Data#conn_data{ + h2_conn = H2Conn, + h2_mon = Mon, + h2_streams = #{} + }, + case Origin of + first_connect -> + {next_state, connected, NewData, + [{reply, From, ok}]}; + after_upgrade -> + {keep_state, NewData, + [{reply, From, ok}]} + end; + {error, WaitErr} -> + catch h2_connection:close(H2Conn), + h2_start_failure(Origin, From, WaitErr) + end; + {error, ActivateErr} -> + catch h2_connection:close(H2Conn), + h2_start_failure(Origin, From, ActivateErr) end; {error, Reason} -> - {keep_state_and_data, [{reply, From, {error, Reason}}]} + h2_start_failure(Origin, From, Reason) end. -%% @private Send an HTTP/2 request -%% Creates a stream, sends HEADERS and optionally DATA frames +h2_start_failure(first_connect, From, Reason) -> + {stop_and_reply, normal, [{reply, From, {error, Reason}}]}; +h2_start_failure(after_upgrade, From, Reason) -> + {keep_state_and_data, [{reply, From, {error, Reason}}]}. + +%% @private Send an HTTP/2 request via the h2 library. do_h2_request(From, Method, Path, Headers, Body, Data) -> - #conn_data{ - transport = Transport, - socket = Socket, - host = Host, - port = Port, - h2_machine = H2Machine0 - } = Data, + do_h2_send(From, Method, Path, Headers, Body, + {sync, waiting_headers}, sync, Data). - %% Initialize a new stream - MethodBin = to_binary(Method), - {ok, StreamId, H2Machine1} = hackney_http2_machine:init_stream(MethodBin, H2Machine0), +%% @private Send an HTTP/2 async request. +do_h2_request_async(From, Method, Path, Headers, Body, AsyncMode, StreamTo, + _FollowRedirect, Data) -> + Ref = self(), + StreamState = {async, AsyncMode, StreamTo, Ref, waiting_headers}, + do_h2_send(From, Method, Path, Headers, Body, StreamState, + {async, Ref, StreamTo, AsyncMode}, Data). - %% Build pseudo-headers - Scheme = case Transport of - hackney_ssl -> <<"https">>; - _ -> <<"http">> - end, - Authority = build_authority(Host, Port, Transport), +do_h2_send(From, Method, Path, Headers, Body, StreamState, Mode, Data) -> + #conn_data{h2_conn = H2Conn, transport = Transport, host = Host, port = Port} = Data, + MethodBin = to_binary(Method), PathBin = case Path of <<>> -> <<"/">>; _ -> to_binary(Path) end, - - PseudoHeaders = #{ - method => MethodBin, - scheme => Scheme, - authority => Authority, - path => PathBin - }, - - %% Convert headers to binary tuples - H2Headers = normalize_headers(Headers), - - %% Determine if this is the final frame (no body) - IsFin = case Body of - <<>> -> fin; - [] -> fin; - _ -> nofin - end, - - %% Prepare headers using h2_machine - {ok, _IsFin2, HeaderBlock, H2Machine2} = hackney_http2_machine:prepare_headers(StreamId, H2Machine1, IsFin, PseudoHeaders, H2Headers), - %% Build and send HEADERS frame - HeadersFrame = hackney_http2:headers(StreamId, IsFin, HeaderBlock), - case Transport:send(Socket, HeadersFrame) of - ok -> - %% Send body if present - case send_h2_body(Transport, Socket, StreamId, Body, H2Machine2) of - {ok, H2Machine3} -> - %% Track stream for response - Streams = maps:put(StreamId, {From, waiting_response}, Data#conn_data.h2_streams), - NewData = Data#conn_data{ - h2_machine = H2Machine3, - h2_streams = Streams, - method = MethodBin, - path = PathBin, - request_from = From - }, - %% Set socket to active mode for receiving response - case Transport:setopts(Socket, [{active, once}]) of - ok -> - %% Stay in connected state, wait for response via info messages - {keep_state, NewData}; - {error, SetoptsErr} -> - {next_state, closed, NewData#conn_data{socket = undefined}, - [{reply, From, {error, SetoptsErr}}]} - end; - {error, Reason} -> - {keep_state_and_data, [{reply, From, {error, Reason}}]} - end; - {error, Reason} -> - {keep_state_and_data, [{reply, From, {error, Reason}}]} - end. - -%% @private Send an HTTP/2 async request -%% Creates a stream, sends HEADERS and optionally DATA frames, returns Ref immediately -do_h2_request_async(From, Method, Path, Headers, Body, AsyncMode, StreamTo, _FollowRedirect, Data) -> - #conn_data{ - transport = Transport, - socket = Socket, - host = Host, - port = Port, - h2_machine = H2Machine0 - } = Data, - - %% Use self() (connection PID) as the async ref for message correlation - Ref = self(), - - %% Initialize a new stream - MethodBin = to_binary(Method), - {ok, StreamId, H2Machine1} = hackney_http2_machine:init_stream(MethodBin, H2Machine0), - - %% Build pseudo-headers Scheme = case Transport of hackney_ssl -> <<"https">>; _ -> <<"http">> end, Authority = build_authority(Host, Port, Transport), - PathBin = case Path of - <<>> -> <<"/">>; - _ -> to_binary(Path) + PseudoHeaders = [ + {<<":method">>, MethodBin}, + {<<":scheme">>, Scheme}, + {<<":authority">>, Authority}, + {<<":path">>, PathBin} + ], + H2Headers = PseudoHeaders ++ normalize_headers(Headers), + BodyBin = case Body of + B when is_binary(B) -> B; + L -> iolist_to_binary(L) end, - - PseudoHeaders = #{ - method => MethodBin, - scheme => Scheme, - authority => Authority, - path => PathBin - }, - - %% Convert headers to binary tuples - H2Headers = normalize_headers(Headers), - - %% Determine if this is the final frame (no body) - IsFin = case Body of - <<>> -> fin; - [] -> fin; - _ -> nofin + SendRes = case BodyBin of + <<>> -> h2_connection:send_request_headers(H2Conn, H2Headers, true); + _ -> + case h2_connection:send_request_headers(H2Conn, H2Headers, false) of + {ok, SId} -> + case h2_connection:send_data(H2Conn, SId, BodyBin, true) of + ok -> {ok, SId}; + {error, _} = E1 -> E1 + end; + Err -> Err + end end, - - %% Prepare headers using h2_machine - {ok, _IsFin2, HeaderBlock, H2Machine2} = hackney_http2_machine:prepare_headers(StreamId, H2Machine1, IsFin, PseudoHeaders, H2Headers), - %% Build and send HEADERS frame - HeadersFrame = hackney_http2:headers(StreamId, IsFin, HeaderBlock), - case Transport:send(Socket, HeadersFrame) of - ok -> - %% Send body if present - case send_h2_body(Transport, Socket, StreamId, Body, H2Machine2) of - {ok, H2Machine3} -> - %% Track stream with async state - StreamState = {waiting_headers_async, AsyncMode, StreamTo, Ref}, - Streams = maps:put(StreamId, {StreamTo, StreamState}, Data#conn_data.h2_streams), - NewData = Data#conn_data{ - h2_machine = H2Machine3, - h2_streams = Streams, - method = MethodBin, - path = PathBin, + case SendRes of + {ok, StreamId} -> + Streams = maps:put(StreamId, {From, StreamState}, + Data#conn_data.h2_streams), + NewData0 = Data#conn_data{ + h2_streams = Streams, + method = MethodBin, + path = PathBin + }, + NewData = case Mode of + sync -> + NewData0#conn_data{request_from = From}; + {async, Ref, StreamTo, AsyncMode} -> + NewData0#conn_data{ async = AsyncMode, async_ref = Ref, stream_to = StreamTo - }, - %% Set socket to active mode for receiving response - case Transport:setopts(Socket, [{active, once}]) of - ok -> - %% Reply immediately with {ok, Ref} - {keep_state, NewData, [{reply, From, {ok, Ref}}]}; - {error, SetoptsErr} -> - {next_state, closed, NewData#conn_data{socket = undefined}, - [{reply, From, {error, SetoptsErr}}]} - end; - {error, Reason} -> - {keep_state_and_data, [{reply, From, {error, Reason}}]} + } + end, + case Mode of + sync -> {keep_state, NewData}; + {async, Ref1, _, _} -> {keep_state, NewData, [{reply, From, {ok, Ref1}}]} end; {error, Reason} -> {keep_state_and_data, [{reply, From, {error, Reason}}]} end. -%% @private Send HTTP/2 request body as DATA frames with flow control -send_h2_body(_Transport, _Socket, _StreamId, <<>>, H2Machine) -> - {ok, H2Machine}; -send_h2_body(_Transport, _Socket, _StreamId, [], H2Machine) -> - {ok, H2Machine}; -send_h2_body(Transport, Socket, StreamId, Body, H2Machine) -> - BodyBin = iolist_to_binary(Body), - %% Use the HTTP/2 machine to handle flow control - case hackney_http2_machine:send_or_queue_data(StreamId, H2Machine, fin, {data, BodyBin}) of - {ok, H2Machine1} -> - %% Data was queued (flow control window exhausted) - %% For simple requests, this shouldn't happen often - {ok, H2Machine1}; - {send, SendList, H2Machine1} -> - %% Send all the data frames - send_h2_data_frames(Transport, Socket, SendList, H2Machine1) - end. - -%% @private Send the DATA frames produced by the HTTP/2 machine -send_h2_data_frames(_Transport, _Socket, [], H2Machine) -> - {ok, H2Machine}; -send_h2_data_frames(Transport, Socket, [{StreamId, IsFin, DataList} | Rest], H2Machine) -> - case send_h2_data_list(Transport, Socket, StreamId, IsFin, DataList) of - ok -> send_h2_data_frames(Transport, Socket, Rest, H2Machine); - {error, Reason} -> {error, Reason} - end. - -%% @private Send individual data items for a stream -send_h2_data_list(_Transport, _Socket, _StreamId, _IsFin, []) -> - ok; -send_h2_data_list(Transport, Socket, StreamId, IsFin, [Item | Rest]) -> - %% Determine if this is the last item - ItemIsFin = case Rest of - [] -> IsFin; - _ -> nofin - end, - case Item of - {data, Data} -> - DataFrame = hackney_http2:data(StreamId, ItemIsFin, Data), - case Transport:send(Socket, DataFrame) of - ok -> send_h2_data_list(Transport, Socket, StreamId, IsFin, Rest); - {error, Reason} -> {error, Reason} - end; - {trailers, _Trailers} -> - %% Trailers not supported in simple body send - send_h2_data_list(Transport, Socket, StreamId, IsFin, Rest) - end. - %% @private Build authority (host:port) for HTTP/2 :authority pseudo-header build_authority(Host, Port, Transport) -> HostBin = to_binary(Host), @@ -2574,407 +2429,139 @@ normalize_headers(Headers) -> end end, Headers). -%% @private Handle incoming HTTP/2 data -%% Parses frames and processes them through the h2_machine -handle_h2_data(RecvData, Data) -> - #conn_data{buffer = Buffer, transport = Transport, socket = Socket} = Data, - FullData = <>, - case parse_h2_frames(FullData, Data#conn_data{buffer = <<>>}) of - {ok, NewData} -> - %% Continue receiving if we have pending streams - case maps:size(NewData#conn_data.h2_streams) > 0 of - true -> - case Transport:setopts(Socket, [{active, once}]) of - ok -> {keep_state, NewData}; - {error, _} -> {next_state, closed, NewData#conn_data{socket = undefined}} - end; - false -> - {keep_state, NewData} - end; - {reply, Reply, NewData} -> - %% Reply to caller and continue - case NewData#conn_data.request_from of - undefined -> - {keep_state, NewData}; - From -> - case Transport:setopts(Socket, [{active, once}]) of - ok -> - {keep_state, NewData#conn_data{request_from = undefined}, [{reply, From, Reply}]}; - {error, _} -> - {next_state, closed, NewData#conn_data{socket = undefined, request_from = undefined}, - [{reply, From, Reply}]} - end - end; - {error, Reason, NewData} -> - %% Error - reply and close - case NewData#conn_data.request_from of - undefined -> - {next_state, closed, NewData}; - From -> - {next_state, closed, NewData, [{reply, From, {error, Reason}}]} - end - end. - -%% @private Parse HTTP/2 frames from buffer -parse_h2_frames(Buffer, Data) -> - case hackney_http2:parse(Buffer) of - {ok, Frame, Rest} -> - case handle_h2_frame(Frame, Data) of - {ok, NewData} -> - parse_h2_frames(Rest, NewData); - {reply, Reply, NewData} -> - {reply, Reply, NewData#conn_data{buffer = Rest}}; - {error, Reason, NewData} -> - {error, Reason, NewData} - end; - more -> - {ok, Data#conn_data{buffer = Buffer}}; - {connection_error, ErrorCode, _Reason} -> - {error, {h2_connection_error, ErrorCode}, Data} - end. - -%% @private Handle individual HTTP/2 frames -handle_h2_frame({settings, Settings}, Data) -> - %% Server SETTINGS frame - acknowledge and update machine - #conn_data{h2_machine = H2Machine, transport = Transport, socket = Socket} = Data, - case hackney_http2_machine:frame({settings, Settings}, H2Machine) of - {ok, H2Machine2} -> - %% Send SETTINGS ACK - SettingsAck = hackney_http2:settings_ack(), - case Transport:send(Socket, SettingsAck) of - ok -> {ok, Data#conn_data{h2_machine = H2Machine2}}; - {error, Reason} -> {error, Reason, Data} - end; - {error, Reason, _H2Machine} -> - {error, Reason, Data} - end; - -handle_h2_frame(settings_ack, Data) -> - %% Server acknowledged our SETTINGS - #conn_data{h2_machine = H2Machine} = Data, - case hackney_http2_machine:frame(settings_ack, H2Machine) of - {ok, H2Machine2} -> - {ok, Data#conn_data{h2_machine = H2Machine2}}; - {error, Reason, _H2Machine} -> - {error, Reason, Data} - end; - -handle_h2_frame({headers, StreamId, IsFin, HeadFin, HeaderBlock}, Data) -> - %% Response headers received (5-tuple format - no priority) - %% Convert to 8-tuple format expected by h2_machine - %% DepStreamId must be positive (1 means no actual dependency) - Frame = {headers, StreamId, IsFin, HeadFin, shared, 1, 16, HeaderBlock}, - do_handle_h2_headers(StreamId, IsFin, Frame, Data); -handle_h2_frame({headers, StreamId, IsFin, HeadFin, Exclusive, DepStreamId, Weight, HeaderBlock}, Data) -> - %% Response headers received (8-tuple format - with priority) - Frame = {headers, StreamId, IsFin, HeadFin, Exclusive, DepStreamId, Weight, HeaderBlock}, - do_handle_h2_headers(StreamId, IsFin, Frame, Data); - -handle_h2_frame({data, StreamId, IsFin, BodyData}, Data) -> - %% Response body data received - #conn_data{h2_machine = H2Machine} = Data, - case hackney_http2_machine:frame({data, StreamId, IsFin, BodyData}, H2Machine) of - {ok, {data, StreamId, RespIsFin, RespData}, H2Machine2} -> - %% h2_machine may return processed data - do_handle_h2_data_body(StreamId, RespIsFin, RespData, H2Machine2, Data); - {ok, H2Machine2} -> - %% Simple ack from machine - do_handle_h2_data_body(StreamId, IsFin, BodyData, H2Machine2, Data); - {error, Reason, _H2Machine} -> - {error, Reason, Data} - end; - -handle_h2_frame({ping, Opaque}, Data) -> - %% Respond to PING with PING ACK - #conn_data{transport = Transport, socket = Socket, h2_machine = H2Machine} = Data, - case hackney_http2_machine:frame({ping, Opaque}, H2Machine) of - {ok, H2Machine2} -> - PingAck = hackney_http2:ping_ack(Opaque), - Transport:send(Socket, PingAck), - {ok, Data#conn_data{h2_machine = H2Machine2}}; - {error, Reason, _H2Machine} -> - {error, Reason, Data} - end; - -handle_h2_frame({ping_ack, _Opaque}, Data) -> - %% PING ACK received - ignore - #conn_data{h2_machine = H2Machine} = Data, - case hackney_http2_machine:frame({ping_ack, _Opaque}, H2Machine) of - {ok, H2Machine2} -> - {ok, Data#conn_data{h2_machine = H2Machine2}}; - {error, Reason, _H2Machine} -> - {error, Reason, Data} - end; - -handle_h2_frame({goaway, LastStreamId, ErrorCode, _DebugData}, Data) -> - %% Server is going away - #conn_data{h2_machine = H2Machine} = Data, - case hackney_http2_machine:frame({goaway, LastStreamId, ErrorCode, _DebugData}, H2Machine) of - {ok, {goaway, _, _, _}, H2Machine2} -> - {error, {goaway, ErrorCode}, Data#conn_data{h2_machine = H2Machine2}}; - {ok, H2Machine2} -> - {error, {goaway, ErrorCode}, Data#conn_data{h2_machine = H2Machine2}}; - {error, Reason, _H2Machine} -> - {error, Reason, Data} - end; - -handle_h2_frame({window_update, Increment}, Data) -> - %% Connection-level window update - #conn_data{h2_machine = H2Machine, transport = Transport, socket = Socket} = Data, - case hackney_http2_machine:frame({window_update, Increment}, H2Machine) of - {ok, H2Machine2} -> - {ok, Data#conn_data{h2_machine = H2Machine2}}; - {send, SendList, H2Machine2} -> - case send_h2_data_frames(Transport, Socket, SendList, H2Machine2) of - {ok, H2Machine3} -> - {ok, Data#conn_data{h2_machine = H2Machine3}}; - {error, Reason} -> - {error, Reason, Data} - end; - {error, Reason, _H2Machine} -> - {error, Reason, Data} - end; - -handle_h2_frame({window_update, StreamId, Increment}, Data) -> - %% Stream-level window update - #conn_data{h2_machine = H2Machine, transport = Transport, socket = Socket} = Data, - case hackney_http2_machine:frame({window_update, StreamId, Increment}, H2Machine) of - {ok, H2Machine2} -> - {ok, Data#conn_data{h2_machine = H2Machine2}}; - {send, SendList, H2Machine2} -> - case send_h2_data_frames(Transport, Socket, SendList, H2Machine2) of - {ok, H2Machine3} -> - {ok, Data#conn_data{h2_machine = H2Machine3}}; - {error, Reason} -> - {error, Reason, Data} - end; - {error, Reason, _H2Machine} -> - {error, Reason, Data} - end; - -handle_h2_frame({rst_stream, StreamId, ErrorCode}, Data) -> - %% Stream reset +%% @private Handle a {h2, Conn, Event} owner message from the h2 library. +handle_h2_event({informational, _StreamId, _Status, _Headers}, Data) -> + %% 1xx interim responses: ignore for now (same as previous HPACK path). + {keep_state, Data}; +handle_h2_event({response, StreamId, Status, Headers}, Data) -> + h2_on_response(StreamId, Status, Headers, Data); +handle_h2_event({data, StreamId, Body, EndStream}, Data) -> + h2_on_data(StreamId, Body, EndStream, Data); +handle_h2_event({trailers, _StreamId, _Headers}, Data) -> + {keep_state, Data}; +handle_h2_event({stream_reset, StreamId, ErrorCode}, Data) -> + h2_on_stream_reset(StreamId, ErrorCode, Data); +handle_h2_event({goaway, _LastStreamId, ErrorCode}, Data) -> + h2_on_goaway(ErrorCode, Data); +handle_h2_event({closed, Reason}, Data) -> + h2_on_closed(Reason, Data); +handle_h2_event(_Other, Data) -> + {keep_state, Data}. + +h2_on_response(StreamId, Status, Headers, Data) -> #conn_data{h2_streams = Streams} = Data, case maps:get(StreamId, Streams, undefined) of - {From, _} -> - Streams2 = maps:remove(StreamId, Streams), - {reply, {error, {stream_error, ErrorCode}}, - Data#conn_data{h2_streams = Streams2, request_from = From}}; - undefined -> - {ok, Data} - end; - -handle_h2_frame({push_promise, StreamId, HeadFin, PromisedStreamId, HeaderBlock}, Data) -> - %% Server is attempting to push a resource - #conn_data{h2_machine = H2Machine, transport = Transport, socket = Socket, - enable_push = EnablePush, h2_streams = Streams} = Data, - Frame = {push_promise, StreamId, HeadFin, PromisedStreamId, HeaderBlock}, - case hackney_http2_machine:frame(Frame, H2Machine) of - {ok, {push_promise, OriginStreamId, PromisedStreamId2, Headers, PseudoHeaders}, H2Machine2} -> - handle_push_promise(EnablePush, OriginStreamId, PromisedStreamId2, Headers, PseudoHeaders, - Streams, H2Machine2, Transport, Socket, Data); - {ok, H2Machine2} -> - %% Machine processed but didn't decode headers - reject - RstFrame = hackney_http2:rst_stream(PromisedStreamId, refused_stream), - Transport:send(Socket, RstFrame), - {ok, Data#conn_data{h2_machine = H2Machine2}}; - {error, Reason, _H2Machine} -> - {error, Reason, Data} - end; + {From, {sync, waiting_headers}} -> + %% Headers alone with END_STREAM arrive here when the server + %% already finished the response (body sent via empty data). + Streams2 = maps:put(StreamId, + {From, {sync, body, Status, Headers, <<>>}}, + Streams), + {keep_state, Data#conn_data{h2_streams = Streams2, + status = Status, + response_headers = Headers}}; + {StreamTo, {async, AsyncMode, StreamTo, Ref, waiting_headers}} -> + StreamTo ! {hackney_response, Ref, {status, Status, <<>>}}, + StreamTo ! {hackney_response, Ref, {headers, Headers}}, + Streams2 = maps:put(StreamId, + {StreamTo, {async, AsyncMode, StreamTo, Ref, + streaming, Status, Headers}}, + Streams), + {keep_state, Data#conn_data{h2_streams = Streams2, + status = Status, + response_headers = Headers}}; + _ -> + {keep_state, Data} + end. -handle_h2_frame(_Frame, Data) -> - %% Unknown or unhandled frame - ignore - {ok, Data}. - -%% @private Handle PUSH_PROMISE based on enable_push setting -handle_push_promise(false, _OriginStreamId, PromisedStreamId, _Headers, _PseudoHeaders, - _Streams, H2Machine, Transport, Socket, Data) -> - %% Push disabled - reject with RST_STREAM (REFUSED_STREAM = 0x7) - RstFrame = hackney_http2:rst_stream(PromisedStreamId, refused_stream), - Transport:send(Socket, RstFrame), - {ok, Data#conn_data{h2_machine = H2Machine}}; -handle_push_promise(PushHandler, OriginStreamId, PromisedStreamId, Headers, PseudoHeaders, - Streams, H2Machine, _Transport, _Socket, Data) when is_pid(PushHandler) -> - %% Push enabled - notify handler and track the promised stream - %% PseudoHeaders contains request pseudo-headers: :method, :scheme, :authority, :path - Method = maps:get(method, PseudoHeaders, <<>>), - Path = maps:get(path, PseudoHeaders, <<>>), - Scheme = maps:get(scheme, PseudoHeaders, <<>>), - Authority = maps:get(authority, PseudoHeaders, <<>>), - %% Build push info - PushInfo = #{ - origin_stream => OriginStreamId, - promised_stream => PromisedStreamId, - method => Method, - path => Path, - scheme => Scheme, - authority => Authority, - headers => Headers - }, - %% Notify push handler - %% Message format: {hackney_push, ConnPid, PushInfo} - PushHandler ! {hackney_push, self(), PushInfo}, - %% Track the promised stream - we'll receive headers and data for it - Streams2 = maps:put(PromisedStreamId, {PushHandler, {push, PushInfo}}, Streams), - {ok, Data#conn_data{h2_machine = H2Machine, h2_streams = Streams2}}. - -%% @private Handle DATA frame body after h2_machine processing -do_handle_h2_data_body(StreamId, IsFin, BodyData, H2Machine0, Data) -> - #conn_data{h2_streams = Streams, transport = Transport, socket = Socket} = Data, - DataLen = byte_size(BodyData), - %% Check connection-level flow control - {H2Machine1, _} = maybe_send_window_update(connection, DataLen, H2Machine0, Transport, Socket), - %% Check stream-level flow control (if stream is still open) - {H2Machine, _} = case IsFin of - fin -> {H2Machine1, ok}; - nofin -> maybe_send_window_update(StreamId, DataLen, H2Machine1, Transport, Socket) - end, - %% Update stream with body data +h2_on_data(StreamId, Body, EndStream, Data) -> + #conn_data{h2_streams = Streams} = Data, case maps:get(StreamId, Streams, undefined) of - {From, {waiting_body, Status, Headers, AccBody}} -> - NewBody = <>, - case IsFin of - fin -> - %% Body complete - reply - Streams2 = maps:remove(StreamId, Streams), - {reply, {ok, Status, Headers, NewBody}, - Data#conn_data{h2_machine = H2Machine, h2_streams = Streams2, - status = Status, response_headers = Headers, - request_from = From}}; - nofin -> - %% More body data expected - Streams2 = maps:put(StreamId, {From, {waiting_body, Status, Headers, NewBody}}, Streams), - {ok, Data#conn_data{h2_machine = H2Machine, h2_streams = Streams2}} - end; - {PushHandler, {push_body, Status, RespHeaders, AccBody}} when is_pid(PushHandler) -> - %% Pushed stream body data - NewBody = <>, - case IsFin of - fin -> - %% Push body complete - notify handler + {From, {sync, body, Status, Headers, Acc}} -> + NewAcc = <>, + case EndStream of + true -> Streams2 = maps:remove(StreamId, Streams), - PushHandler ! {hackney_push_response, self(), StreamId, Status, RespHeaders, NewBody}, - {ok, Data#conn_data{h2_machine = H2Machine, h2_streams = Streams2}}; - nofin -> - %% More body data expected - Streams2 = maps:put(StreamId, {PushHandler, {push_body, Status, RespHeaders, NewBody}}, Streams), - {ok, Data#conn_data{h2_machine = H2Machine, h2_streams = Streams2}} + {keep_state, + Data#conn_data{h2_streams = Streams2, + request_from = undefined}, + [{reply, From, {ok, Status, Headers, NewAcc}}]}; + false -> + Streams2 = maps:put(StreamId, + {From, {sync, body, Status, Headers, NewAcc}}, + Streams), + {keep_state, Data#conn_data{h2_streams = Streams2}} end; - {StreamTo, {streaming_body_async, AsyncMode, StreamTo, Ref, Status, Headers}} when is_pid(StreamTo) -> - %% Async mode - send body chunk to StreamTo - _ = case byte_size(BodyData) > 0 of - true -> StreamTo ! {hackney_response, Ref, BodyData}; - false -> ok + {StreamTo, {async, AsyncMode, StreamTo, Ref, streaming, Status, Headers}} -> + _ = case byte_size(Body) of + 0 -> ok; + _ -> StreamTo ! {hackney_response, Ref, Body} end, - case IsFin of - fin -> - %% Body complete - send done + case EndStream of + true -> StreamTo ! {hackney_response, Ref, done}, Streams2 = maps:remove(StreamId, Streams), - {ok, Data#conn_data{h2_machine = H2Machine, h2_streams = Streams2, - async = false, async_ref = undefined, stream_to = undefined}}; - nofin -> - %% More body data expected - keep streaming - NewStreamState = {streaming_body_async, AsyncMode, StreamTo, Ref, Status, Headers}, - Streams2 = maps:put(StreamId, {StreamTo, NewStreamState}, Streams), - {ok, Data#conn_data{h2_machine = H2Machine, h2_streams = Streams2}} + {keep_state, + Data#conn_data{h2_streams = Streams2, + async = false, + async_ref = undefined, + stream_to = undefined}}; + false -> + NewState = {async, AsyncMode, StreamTo, Ref, streaming, + Status, Headers}, + Streams2 = maps:put(StreamId, {StreamTo, NewState}, Streams), + {keep_state, Data#conn_data{h2_streams = Streams2}} end; _ -> - {ok, Data#conn_data{h2_machine = H2Machine}} + {keep_state, Data} end. -%% @private Send WINDOW_UPDATE if needed for flow control -%% Type can be 'connection' for connection-level or StreamId for stream-level -maybe_send_window_update(connection, DataLen, H2Machine, Transport, Socket) -> - %% Check connection-level window - case hackney_http2_machine:ensure_window(DataLen, H2Machine) of - ok -> - {H2Machine, ok}; - {ok, Increment, H2Machine2} -> - %% Send connection-level WINDOW_UPDATE - Frame = hackney_http2:window_update(Increment), - _ = Transport:send(Socket, Frame), - {H2Machine2, ok} - end; -maybe_send_window_update(StreamId, DataLen, H2Machine, Transport, Socket) when is_integer(StreamId) -> - %% Check stream-level window - case hackney_http2_machine:ensure_window(StreamId, DataLen, H2Machine) of - ok -> - {H2Machine, ok}; - {ok, Increment, H2Machine2} -> - %% Send stream-level WINDOW_UPDATE - Frame = hackney_http2:window_update(StreamId, Increment), - _ = Transport:send(Socket, Frame), - {H2Machine2, ok} +h2_on_stream_reset(StreamId, ErrorCode, Data) -> + #conn_data{h2_streams = Streams} = Data, + case maps:get(StreamId, Streams, undefined) of + {From, {sync, _}} -> + Streams2 = maps:remove(StreamId, Streams), + {keep_state, + Data#conn_data{h2_streams = Streams2, request_from = undefined}, + [{reply, From, {error, {stream_error, ErrorCode}}}]}; + {StreamTo, {async, _, StreamTo, Ref, _, _, _}} -> + StreamTo ! {hackney_response, Ref, {error, {stream_error, ErrorCode}}}, + Streams2 = maps:remove(StreamId, Streams), + {keep_state, Data#conn_data{h2_streams = Streams2}}; + {StreamTo, {async, _, StreamTo, Ref, _}} -> + StreamTo ! {hackney_response, Ref, {error, {stream_error, ErrorCode}}}, + Streams2 = maps:remove(StreamId, Streams), + {keep_state, Data#conn_data{h2_streams = Streams2}}; + _ -> + {keep_state, Data} end. -%% @private Handle HTTP/2 headers response -do_handle_h2_headers(StreamId, _IsFin, Frame, Data) -> - #conn_data{h2_machine = H2Machine, h2_streams = Streams} = Data, - case hackney_http2_machine:frame(Frame, H2Machine) of - {ok, {headers, StreamId, RespIsFin, Headers, PseudoHeaders, _Len}, H2Machine2} -> - %% Response headers received - %% PseudoHeaders contains #{status => Status} for responses - Status = maps:get(status, PseudoHeaders, 200), - case maps:get(StreamId, Streams, undefined) of - {From, waiting_response} -> - case RespIsFin of - fin -> - %% No body - reply immediately - Streams2 = maps:remove(StreamId, Streams), - {reply, {ok, Status, Headers}, - Data#conn_data{h2_machine = H2Machine2, h2_streams = Streams2, - status = Status, response_headers = Headers, - request_from = From}}; - nofin -> - %% Body follows - update stream state - Streams2 = maps:put(StreamId, {From, {waiting_body, Status, Headers, <<>>}}, Streams), - {ok, Data#conn_data{h2_machine = H2Machine2, h2_streams = Streams2, - request_from = From}} - end; - {_StreamTo, {waiting_headers_async, AsyncMode, StreamTo, Ref}} -> - %% Async mode - send status and headers to StreamTo - StreamTo ! {hackney_response, Ref, {status, Status, <<"">>}}, - StreamTo ! {hackney_response, Ref, {headers, Headers}}, - case RespIsFin of - fin -> - %% No body - send done and clean up - StreamTo ! {hackney_response, Ref, done}, - Streams2 = maps:remove(StreamId, Streams), - {ok, Data#conn_data{h2_machine = H2Machine2, h2_streams = Streams2, - status = Status, response_headers = Headers, - async = false, async_ref = undefined, stream_to = undefined}}; - nofin -> - %% Body follows - update stream state for async body streaming - NewStreamState = {streaming_body_async, AsyncMode, StreamTo, Ref, Status, Headers}, - Streams2 = maps:put(StreamId, {StreamTo, NewStreamState}, Streams), - {ok, Data#conn_data{h2_machine = H2Machine2, h2_streams = Streams2, - status = Status, response_headers = Headers}} - end; - {PushHandler, {push, _PushInfo}} when is_pid(PushHandler) -> - %% Pushed stream response headers received - case RespIsFin of - fin -> - %% No body - notify immediately - Streams2 = maps:remove(StreamId, Streams), - PushHandler ! {hackney_push_response, self(), StreamId, Status, Headers, <<>>}, - {ok, Data#conn_data{h2_machine = H2Machine2, h2_streams = Streams2}}; - nofin -> - %% Body follows - update stream state - Streams2 = maps:put(StreamId, {PushHandler, {push_body, Status, Headers, <<>>}}, Streams), - {ok, Data#conn_data{h2_machine = H2Machine2, h2_streams = Streams2}} - end; - _ -> - {ok, Data#conn_data{h2_machine = H2Machine2}} - end; - {ok, {trailers, StreamId, _TrailerHeaders}, H2Machine2} -> - %% Trailers received - ignore for now - {ok, Data#conn_data{h2_machine = H2Machine2}}; - {ok, H2Machine2} -> - {ok, Data#conn_data{h2_machine = H2Machine2}}; - {error, Reason, _H2Machine} -> - {error, Reason, Data} - end. +h2_on_goaway(ErrorCode, Data) -> + {Replies, Data1} = collect_h2_aborts({goaway, ErrorCode}, Data), + {keep_state, Data1, Replies}. + +h2_on_closed(Reason, Data) -> + {Replies, Data1} = collect_h2_aborts({closed, Reason}, Data), + Stripped = Data1#conn_data{h2_conn = undefined, h2_mon = undefined, + socket = undefined}, + {next_state, closed, Stripped, Replies}. + +collect_h2_aborts(Err, #conn_data{h2_streams = Streams} = Data) -> + Replies = maps:fold(fun + (_SId, {From, {sync, _}}, Acc) -> + [{reply, From, {error, Err}} | Acc]; + (_SId, {From, {sync, body, _, _, _}}, Acc) -> + [{reply, From, {error, Err}} | Acc]; + (_SId, {StreamTo, {async, _, StreamTo, Ref, _}}, Acc) -> + StreamTo ! {hackney_response, Ref, {error, Err}}, + Acc; + (_SId, {StreamTo, {async, _, StreamTo, Ref, _, _, _}}, Acc) -> + StreamTo ! {hackney_response, Ref, {error, Err}}, + Acc; + (_, _, Acc) -> Acc + end, [], Streams), + {Replies, Data#conn_data{h2_streams = #{}, request_from = undefined}}. + %%==================================================================== %% HTTP/3 Support Functions diff --git a/src/hackney_hpack.erl b/src/hackney_hpack.erl deleted file mode 100644 index abfab93b..00000000 --- a/src/hackney_hpack.erl +++ /dev/null @@ -1,726 +0,0 @@ -%%% -*- erlang -*- -%%% -%%% This file is part of hackney released under the Apache 2 license. -%%% See the NOTICE for more information. -%%% -%%% Copyright (c) 2024-2026 Benoit Chesneau -%%% -%%% @doc Optimized HPACK header compression for HTTP/2 (RFC 7541). -%%% -%%% This implementation provides O(1) dynamic table lookups using -%%% a combination of ring buffer for index access and maps for -%%% field/name lookups during encoding. -%%% -%%% Key optimizations: -%%% - Ring buffer (tuple) for O(1) index-based access -%%% - Maps for O(1) field and name lookups -%%% - Pre-computed static table as maps -%%% - Lookup table for Huffman encoding -%%% @end - --module(hackney_hpack). --dialyzer(no_improper_lists). - --export([init/0]). --export([init/1]). --export([set_max_size/2]). - --export([decode/1]). --export([decode/2]). - --export([encode/1]). --export([encode/2]). --export([encode/3]). - -%% Type definitions --type header() :: {binary(), binary()}. --type headers() :: [header()]. --type opts() :: map(). - --export_type([state/0]). --export_type([opts/0]). --export_type([headers/0]). - -%% Include Huffman lookup tables --include("hackney_hpack_huffman.hrl"). - -%% Reuse existing Huffman decode lookup table from cowlib --include("hackney_hpack_huffman_dec.hrl"). - -%% Dynamic table entry size overhead per RFC 7541 Section 4.1 --define(ENTRY_OVERHEAD, 32). - -%% Static table size --define(STATIC_TABLE_SIZE, 61). - -%% Default ring buffer size (number of entries, will grow as needed) --define(DEFAULT_RING_SIZE, 128). - -%% State record with ring buffer + maps for O(1) operations --record(state, { - size = 0 :: non_neg_integer(), - max_size = 4096 :: non_neg_integer(), - configured_max_size = 4096 :: non_neg_integer(), - %% Ring buffer for O(1) index access - %% Entries stored as {EntrySize, {Name, Value}} - entries = {} :: tuple(), - head = 0 :: non_neg_integer(), %% Position of newest entry - count = 0 :: non_neg_integer(), %% Number of entries - %% Maps for O(1) lookup during encoding - field_index = #{} :: #{header() => pos_integer()}, %% {Name, Value} => absolute_index - name_index = #{} :: #{binary() => pos_integer()}, %% Name => absolute_index - %% Monotonic counter for absolute indexing - insert_count = 0 :: non_neg_integer() -}). - --opaque state() :: #state{}. - -%%==================================================================== -%% Static Table (RFC 7541 Appendix A) -%%==================================================================== - -%% Static table as a tuple for O(1) index access --define(STATIC_TABLE, { - {<<":authority">>, <<>>}, - {<<":method">>, <<"GET">>}, - {<<":method">>, <<"POST">>}, - {<<":path">>, <<"/">>}, - {<<":path">>, <<"/index.html">>}, - {<<":scheme">>, <<"http">>}, - {<<":scheme">>, <<"https">>}, - {<<":status">>, <<"200">>}, - {<<":status">>, <<"204">>}, - {<<":status">>, <<"206">>}, - {<<":status">>, <<"304">>}, - {<<":status">>, <<"400">>}, - {<<":status">>, <<"404">>}, - {<<":status">>, <<"500">>}, - {<<"accept-charset">>, <<>>}, - {<<"accept-encoding">>, <<"gzip, deflate">>}, - {<<"accept-language">>, <<>>}, - {<<"accept-ranges">>, <<>>}, - {<<"accept">>, <<>>}, - {<<"access-control-allow-origin">>, <<>>}, - {<<"age">>, <<>>}, - {<<"allow">>, <<>>}, - {<<"authorization">>, <<>>}, - {<<"cache-control">>, <<>>}, - {<<"content-disposition">>, <<>>}, - {<<"content-encoding">>, <<>>}, - {<<"content-language">>, <<>>}, - {<<"content-length">>, <<>>}, - {<<"content-location">>, <<>>}, - {<<"content-range">>, <<>>}, - {<<"content-type">>, <<>>}, - {<<"cookie">>, <<>>}, - {<<"date">>, <<>>}, - {<<"etag">>, <<>>}, - {<<"expect">>, <<>>}, - {<<"expires">>, <<>>}, - {<<"from">>, <<>>}, - {<<"host">>, <<>>}, - {<<"if-match">>, <<>>}, - {<<"if-modified-since">>, <<>>}, - {<<"if-none-match">>, <<>>}, - {<<"if-range">>, <<>>}, - {<<"if-unmodified-since">>, <<>>}, - {<<"last-modified">>, <<>>}, - {<<"link">>, <<>>}, - {<<"location">>, <<>>}, - {<<"max-forwards">>, <<>>}, - {<<"proxy-authenticate">>, <<>>}, - {<<"proxy-authorization">>, <<>>}, - {<<"range">>, <<>>}, - {<<"referer">>, <<>>}, - {<<"refresh">>, <<>>}, - {<<"retry-after">>, <<>>}, - {<<"server">>, <<>>}, - {<<"set-cookie">>, <<>>}, - {<<"strict-transport-security">>, <<>>}, - {<<"transfer-encoding">>, <<>>}, - {<<"user-agent">>, <<>>}, - {<<"vary">>, <<>>}, - {<<"via">>, <<>>}, - {<<"www-authenticate">>, <<>>} -}). - -%% Static table field map for O(1) exact match lookup --define(STATIC_FIELD_MAP, #{ - {<<":authority">>, <<>>} => 1, - {<<":method">>, <<"GET">>} => 2, - {<<":method">>, <<"POST">>} => 3, - {<<":path">>, <<"/">>} => 4, - {<<":path">>, <<"/index.html">>} => 5, - {<<":scheme">>, <<"http">>} => 6, - {<<":scheme">>, <<"https">>} => 7, - {<<":status">>, <<"200">>} => 8, - {<<":status">>, <<"204">>} => 9, - {<<":status">>, <<"206">>} => 10, - {<<":status">>, <<"304">>} => 11, - {<<":status">>, <<"400">>} => 12, - {<<":status">>, <<"404">>} => 13, - {<<":status">>, <<"500">>} => 14, - {<<"accept-charset">>, <<>>} => 15, - {<<"accept-encoding">>, <<"gzip, deflate">>} => 16, - {<<"accept-language">>, <<>>} => 17, - {<<"accept-ranges">>, <<>>} => 18, - {<<"accept">>, <<>>} => 19, - {<<"access-control-allow-origin">>, <<>>} => 20, - {<<"age">>, <<>>} => 21, - {<<"allow">>, <<>>} => 22, - {<<"authorization">>, <<>>} => 23, - {<<"cache-control">>, <<>>} => 24, - {<<"content-disposition">>, <<>>} => 25, - {<<"content-encoding">>, <<>>} => 26, - {<<"content-language">>, <<>>} => 27, - {<<"content-length">>, <<>>} => 28, - {<<"content-location">>, <<>>} => 29, - {<<"content-range">>, <<>>} => 30, - {<<"content-type">>, <<>>} => 31, - {<<"cookie">>, <<>>} => 32, - {<<"date">>, <<>>} => 33, - {<<"etag">>, <<>>} => 34, - {<<"expect">>, <<>>} => 35, - {<<"expires">>, <<>>} => 36, - {<<"from">>, <<>>} => 37, - {<<"host">>, <<>>} => 38, - {<<"if-match">>, <<>>} => 39, - {<<"if-modified-since">>, <<>>} => 40, - {<<"if-none-match">>, <<>>} => 41, - {<<"if-range">>, <<>>} => 42, - {<<"if-unmodified-since">>, <<>>} => 43, - {<<"last-modified">>, <<>>} => 44, - {<<"link">>, <<>>} => 45, - {<<"location">>, <<>>} => 46, - {<<"max-forwards">>, <<>>} => 47, - {<<"proxy-authenticate">>, <<>>} => 48, - {<<"proxy-authorization">>, <<>>} => 49, - {<<"range">>, <<>>} => 50, - {<<"referer">>, <<>>} => 51, - {<<"refresh">>, <<>>} => 52, - {<<"retry-after">>, <<>>} => 53, - {<<"server">>, <<>>} => 54, - {<<"set-cookie">>, <<>>} => 55, - {<<"strict-transport-security">>, <<>>} => 56, - {<<"transfer-encoding">>, <<>>} => 57, - {<<"user-agent">>, <<>>} => 58, - {<<"vary">>, <<>>} => 59, - {<<"via">>, <<>>} => 60, - {<<"www-authenticate">>, <<>>} => 61 -}). - -%% Static table name map for O(1) name-only lookup --define(STATIC_NAME_MAP, #{ - <<":authority">> => 1, - <<":method">> => 2, - <<":path">> => 4, - <<":scheme">> => 6, - <<":status">> => 8, - <<"accept-charset">> => 15, - <<"accept-encoding">> => 16, - <<"accept-language">> => 17, - <<"accept-ranges">> => 18, - <<"accept">> => 19, - <<"access-control-allow-origin">> => 20, - <<"age">> => 21, - <<"allow">> => 22, - <<"authorization">> => 23, - <<"cache-control">> => 24, - <<"content-disposition">> => 25, - <<"content-encoding">> => 26, - <<"content-language">> => 27, - <<"content-length">> => 28, - <<"content-location">> => 29, - <<"content-range">> => 30, - <<"content-type">> => 31, - <<"cookie">> => 32, - <<"date">> => 33, - <<"etag">> => 34, - <<"expect">> => 35, - <<"expires">> => 36, - <<"from">> => 37, - <<"host">> => 38, - <<"if-match">> => 39, - <<"if-modified-since">> => 40, - <<"if-none-match">> => 41, - <<"if-range">> => 42, - <<"if-unmodified-since">> => 43, - <<"last-modified">> => 44, - <<"link">> => 45, - <<"location">> => 46, - <<"max-forwards">> => 47, - <<"proxy-authenticate">> => 48, - <<"proxy-authorization">> => 49, - <<"range">> => 50, - <<"referer">> => 51, - <<"refresh">> => 52, - <<"retry-after">> => 53, - <<"server">> => 54, - <<"set-cookie">> => 55, - <<"strict-transport-security">> => 56, - <<"transfer-encoding">> => 57, - <<"user-agent">> => 58, - <<"vary">> => 59, - <<"via">> => 60, - <<"www-authenticate">> => 61 -}). - -%%==================================================================== -%% State Initialization -%%==================================================================== - --spec init() -> state(). -init() -> - #state{entries = erlang:make_tuple(?DEFAULT_RING_SIZE, undefined)}. - --spec init(non_neg_integer()) -> state(). -init(MaxSize) -> - #state{ - max_size = MaxSize, - configured_max_size = MaxSize, - entries = erlang:make_tuple(?DEFAULT_RING_SIZE, undefined) - }. - --spec set_max_size(non_neg_integer(), State) -> State when State::state(). -set_max_size(MaxSize, State) -> - State#state{configured_max_size = MaxSize}. - -%%==================================================================== -%% Decoding -%%==================================================================== - --spec decode(binary()) -> {headers(), state()}. -decode(Data) -> - decode(Data, init()). - --spec decode(binary(), State) -> {headers(), State} when State::state(). -%% Dynamic table size update is only allowed at the beginning of a HEADERS block. -decode(<<0:2, 1:1, Rest/bits>>, State = #state{configured_max_size = ConfigMaxSize}) -> - {MaxSize, Rest2} = dec_int5(Rest), - if - MaxSize =< ConfigMaxSize -> - State2 = table_update_size(MaxSize, State), - decode(Rest2, State2) - end; -decode(Data, State) -> - decode(Data, State, []). - -decode(<<>>, State, Acc) -> - {lists:reverse(Acc), State}; -%% Indexed header field representation. -decode(<<1:1, Rest/bits>>, State, Acc) -> - dec_indexed(Rest, State, Acc); -%% Literal header field with incremental indexing: new name. -decode(<<0:1, 1:1, 0:6, Rest/bits>>, State, Acc) -> - dec_lit_index_new_name(Rest, State, Acc); -%% Literal header field with incremental indexing: indexed name. -decode(<<0:1, 1:1, Rest/bits>>, State, Acc) -> - dec_lit_index_indexed_name(Rest, State, Acc); -%% Literal header field without indexing: new name. -decode(<<0:8, Rest/bits>>, State, Acc) -> - dec_lit_no_index_new_name(Rest, State, Acc); -%% Literal header field without indexing: indexed name. -decode(<<0:4, Rest/bits>>, State, Acc) -> - dec_lit_no_index_indexed_name(Rest, State, Acc); -%% Literal header field never indexed: new name. -decode(<<0:3, 1:1, 0:4, Rest/bits>>, State, Acc) -> - dec_lit_no_index_new_name(Rest, State, Acc); -%% Literal header field never indexed: indexed name. -decode(<<0:3, 1:1, Rest/bits>>, State, Acc) -> - dec_lit_no_index_indexed_name(Rest, State, Acc). - -%% Indexed header field representation. -dec_indexed(<<2#1111111:7, 0:1, Int:7, Rest/bits>>, State, Acc) -> - {Name, Value} = table_get(127 + Int, State), - decode(Rest, State, [{Name, Value} | Acc]); -dec_indexed(<<2#1111111:7, Rest0/bits>>, State, Acc) -> - {Index, Rest} = dec_big_int(Rest0, 127, 0), - {Name, Value} = table_get(Index, State), - decode(Rest, State, [{Name, Value} | Acc]); -dec_indexed(<>, State, Acc) -> - {Name, Value} = table_get(Index, State), - decode(Rest, State, [{Name, Value} | Acc]). - -%% Literal header field with incremental indexing. -dec_lit_index_new_name(Rest, State, Acc) -> - {Name, Rest2} = dec_str(Rest), - dec_lit_index(Rest2, State, Acc, Name). - -dec_lit_index_indexed_name(<<2#111111:6, 0:1, Int:7, Rest/bits>>, State, Acc) -> - Name = table_get_name(63 + Int, State), - dec_lit_index(Rest, State, Acc, Name); -dec_lit_index_indexed_name(<<2#111111:6, Rest0/bits>>, State, Acc) -> - {Index, Rest} = dec_big_int(Rest0, 63, 0), - Name = table_get_name(Index, State), - dec_lit_index(Rest, State, Acc, Name); -dec_lit_index_indexed_name(<>, State, Acc) -> - Name = table_get_name(Index, State), - dec_lit_index(Rest, State, Acc, Name). - -dec_lit_index(Rest, State, Acc, Name) -> - {Value, Rest2} = dec_str(Rest), - State2 = table_insert({Name, Value}, State), - decode(Rest2, State2, [{Name, Value} | Acc]). - -%% Literal header field without indexing. -dec_lit_no_index_new_name(Rest, State, Acc) -> - {Name, Rest2} = dec_str(Rest), - dec_lit_no_index(Rest2, State, Acc, Name). - -dec_lit_no_index_indexed_name(<<2#1111:4, 0:1, Int:7, Rest/bits>>, State, Acc) -> - Name = table_get_name(15 + Int, State), - dec_lit_no_index(Rest, State, Acc, Name); -dec_lit_no_index_indexed_name(<<2#1111:4, Rest0/bits>>, State, Acc) -> - {Index, Rest} = dec_big_int(Rest0, 15, 0), - Name = table_get_name(Index, State), - dec_lit_no_index(Rest, State, Acc, Name); -dec_lit_no_index_indexed_name(<>, State, Acc) -> - Name = table_get_name(Index, State), - dec_lit_no_index(Rest, State, Acc, Name). - -dec_lit_no_index(Rest, State, Acc, Name) -> - {Value, Rest2} = dec_str(Rest), - decode(Rest2, State, [{Name, Value} | Acc]). - -%% Integer decoding -dec_int5(<<2#11111:5, Rest/bits>>) -> - dec_big_int(Rest, 31, 0); -dec_int5(<>) -> - {Int, Rest}. - -dec_big_int(<<0:1, Value:7, Rest/bits>>, Int, M) -> - {Int + (Value bsl M), Rest}; -dec_big_int(<<1:1, Value:7, Rest/bits>>, Int, M) -> - dec_big_int(Rest, Int + (Value bsl M), M + 7). - -%% String decoding -dec_str(<<0:1, 2#1111111:7, Rest0/bits>>) -> - {Length, Rest1} = dec_big_int(Rest0, 127, 0), - <> = Rest1, - {Str, Rest}; -dec_str(<<0:1, Length:7, Rest0/bits>>) -> - <> = Rest0, - {Str, Rest}; -dec_str(<<1:1, 2#1111111:7, Rest0/bits>>) -> - {Length, Rest} = dec_big_int(Rest0, 127, 0), - dec_huffman(Rest, Length, 0, <<>>); -dec_str(<<1:1, Length:7, Rest/bits>>) -> - dec_huffman(Rest, Length, 0, <<>>). - -%% Huffman decoding using lookup table -dec_huffman(<>, Len, Huff0, Acc) when Len > 1 -> - {_, CharA, Huff1} = dec_huffman_lookup(Huff0, A), - {_, CharB, Huff} = dec_huffman_lookup(Huff1, B), - case {CharA, CharB} of - {undefined, undefined} -> dec_huffman(R, Len - 1, Huff, Acc); - {CharA, undefined} -> dec_huffman(R, Len - 1, Huff, <>); - {undefined, CharB} -> dec_huffman(R, Len - 1, Huff, <>); - {CharA, CharB} -> dec_huffman(R, Len - 1, Huff, <>) - end; -dec_huffman(<>, 1, Huff0, Acc) -> - {_, CharA, Huff} = dec_huffman_lookup(Huff0, A), - {ok, CharB, _} = dec_huffman_lookup(Huff, B), - case {CharA, CharB} of - {CharA, undefined} -> - {<>, Rest}; - {undefined, CharB} -> - {<>, Rest}; - _ -> - {<>, Rest} - end; -dec_huffman(Rest, 0, _, <<>>) -> - {<<>>, Rest}. - -%%==================================================================== -%% Encoding -%%==================================================================== - --spec encode(headers()) -> {iodata(), state()}. -encode(Headers) -> - encode(Headers, init(), huffman, []). - --spec encode(headers(), State) -> {iodata(), State} when State::state(). -encode(Headers, State = #state{max_size = MaxSize, configured_max_size = MaxSize}) -> - encode(Headers, State, huffman, []); -encode(Headers, State0 = #state{configured_max_size = MaxSize}) -> - State1 = table_update_size(MaxSize, State0), - {Data, State} = encode(Headers, State1, huffman, []), - {[enc_int5(MaxSize, 2#001) | Data], State}. - --spec encode(headers(), State, opts()) -> {iodata(), State} when State::state(). -encode(Headers, State = #state{max_size = MaxSize, configured_max_size = MaxSize}, Opts) -> - encode(Headers, State, huffman_opt(Opts), []); -encode(Headers, State0 = #state{configured_max_size = MaxSize}, Opts) -> - State1 = table_update_size(MaxSize, State0), - {Data, State} = encode(Headers, State1, huffman_opt(Opts), []), - {[enc_int5(MaxSize, 2#001) | Data], State}. - -huffman_opt(#{huffman := false}) -> no_huffman; -huffman_opt(_) -> huffman. - -encode([], State, _, Acc) -> - {lists:reverse(Acc), State}; -encode([{Name, Value0} | Tail], State, HuffmanOpt, Acc) -> - Value = if - is_binary(Value0) -> Value0; - true -> iolist_to_binary(Value0) - end, - Header = {Name, Value}, - case table_find(Header, State) of - %% Indexed header field representation. - {field, Index} -> - encode(Tail, State, HuffmanOpt, - [enc_int7(Index, 2#1) | Acc]); - %% Literal header field representation: indexed name. - {name, Index} -> - State2 = table_insert(Header, State), - encode(Tail, State2, HuffmanOpt, - [[enc_int6(Index, 2#01) | enc_str(Value, HuffmanOpt)] | Acc]); - %% Literal header field representation: new name. - not_found -> - State2 = table_insert(Header, State), - encode(Tail, State2, HuffmanOpt, - [[<<0:1, 1:1, 0:6>> | [enc_str(Name, HuffmanOpt) | enc_str(Value, HuffmanOpt)]] | Acc]) - end. - -%% Integer encoding -enc_int5(Int, Prefix) when Int < 31 -> - <>; -enc_int5(Int, Prefix) -> - enc_big_int(Int - 31, <>). - -enc_int6(Int, Prefix) when Int < 63 -> - <>; -enc_int6(Int, Prefix) -> - enc_big_int(Int - 63, <>). - -enc_int7(Int, Prefix) when Int < 127 -> - <>; -enc_int7(Int, Prefix) -> - enc_big_int(Int - 127, <>). - -enc_big_int(Int, Acc) when Int < 128 -> - <>; -enc_big_int(Int, Acc) -> - enc_big_int(Int bsr 7, <>). - -%% String encoding -enc_str(Str, huffman) -> - Encoded = enc_huffman(Str), - [enc_int7(byte_size(Encoded), 2#1) | Encoded]; -enc_str(Str, no_huffman) -> - [enc_int7(byte_size(Str), 2#0) | Str]. - -%% Huffman encoding using lookup table -enc_huffman(Str) -> - enc_huffman(Str, <<>>). - -enc_huffman(<<>>, Acc) -> - %% Add EOS padding - case bit_size(Acc) rem 8 of - 0 -> Acc; - N -> - Padding = 8 - N, - PadBits = (1 bsl Padding) - 1, - <> - end; -enc_huffman(<>, Acc) -> - {Code, Bits} = ?HUFFMAN_CODE(Byte), - enc_huffman(Rest, <>). - -%%==================================================================== -%% Table Operations -%%==================================================================== - -%% Find header in table - O(1) using maps -table_find(Header = {Name, _Value}, State) -> - %% First check static table for exact match - case maps:find(Header, ?STATIC_FIELD_MAP) of - {ok, Index} -> - {field, Index}; - error -> - %% Check dynamic table for exact match - case find_in_dynamic_field(Header, State) of - {ok, Index} -> - {field, Index}; - error -> - %% Check static table for name match - case maps:find(Name, ?STATIC_NAME_MAP) of - {ok, Index} -> - {name, Index}; - error -> - %% Check dynamic table for name match - case find_in_dynamic_name(Name, State) of - {ok, Index} -> - {name, Index}; - error -> - not_found - end - end - end - end. - -%% Find exact field match in dynamic table -find_in_dynamic_field(Header, #state{field_index = FieldIndex, insert_count = InsertCount, count = Count}) -> - case maps:find(Header, FieldIndex) of - {ok, AbsIndex} when InsertCount - AbsIndex < Count -> - %% Convert absolute index to relative index - %% Relative index = insert_count - abs_index + STATIC_TABLE_SIZE - {ok, ?STATIC_TABLE_SIZE + (InsertCount - AbsIndex) + 1}; - _ -> - error - end. - -%% Find name match in dynamic table -find_in_dynamic_name(Name, #state{name_index = NameIndex, insert_count = InsertCount, count = Count}) -> - case maps:find(Name, NameIndex) of - {ok, AbsIndex} when InsertCount - AbsIndex < Count -> - {ok, ?STATIC_TABLE_SIZE + (InsertCount - AbsIndex) + 1}; - _ -> - error - end. - -%% Get entry by index (1-based) - O(1) -table_get(Index, _State) when Index >= 1, Index =< ?STATIC_TABLE_SIZE -> - element(Index, ?STATIC_TABLE); -table_get(Index, State) when Index > ?STATIC_TABLE_SIZE -> - DynIndex = Index - ?STATIC_TABLE_SIZE, - get_dynamic_entry(DynIndex, State). - -%% Get name by index (1-based) - O(1) -table_get_name(Index, State) -> - {Name, _Value} = table_get(Index, State), - Name. - -%% Get dynamic table entry by relative index (1 = newest) -get_dynamic_entry(RelIndex, #state{entries = Entries, head = Head, count = Count}) - when RelIndex >= 1, RelIndex =< Count -> - RingSize = tuple_size(Entries), - %% RelIndex 1 is at head, RelIndex 2 is at head-1, etc. - Pos = ((Head - RelIndex + RingSize) rem RingSize) + 1, - {_Size, Header} = element(Pos, Entries), - Header. - -%% Insert entry into dynamic table - O(1) amortized -table_insert(Entry = {Name, Value}, State = #state{ - size = Size, - max_size = MaxSize, - insert_count = InsertCount -}) -> - EntrySize = byte_size(Name) + byte_size(Value) + ?ENTRY_OVERHEAD, - if - EntrySize > MaxSize -> - %% Entry larger than table - clear everything - State#state{ - size = 0, - head = 0, - count = 0, - field_index = #{}, - name_index = #{}, - insert_count = InsertCount + 1 - }; - EntrySize + Size =< MaxSize -> - %% Add entry without eviction - add_entry(Entry, EntrySize, State); - true -> - %% Need to evict entries first - State2 = evict_entries(State, EntrySize), - add_entry(Entry, EntrySize, State2) - end. - -%% Add entry to ring buffer -add_entry(Entry = {Name, _Value}, EntrySize, State = #state{ - size = Size, - entries = Entries, - head = Head, - count = Count, - field_index = FieldIndex, - name_index = NameIndex, - insert_count = InsertCount -}) -> - RingSize = tuple_size(Entries), - NewInsertCount = InsertCount + 1, - %% Calculate new head position - NewHead = (Head rem RingSize) + 1, - %% Grow ring buffer if needed - {NewEntries, _NewRingSize} = case Count >= RingSize of - true -> - %% Double the ring buffer size - grow_ring_buffer(Entries, RingSize); - false -> - {Entries, RingSize} - end, - %% Insert entry - NewEntries2 = setelement(NewHead, NewEntries, {EntrySize, Entry}), - State#state{ - size = Size + EntrySize, - entries = NewEntries2, - head = NewHead, - count = Count + 1, - field_index = FieldIndex#{Entry => NewInsertCount}, - name_index = NameIndex#{Name => NewInsertCount}, - insert_count = NewInsertCount - }. - -%% Grow ring buffer by doubling its size -grow_ring_buffer(Entries, OldSize) -> - NewSize = OldSize * 2, - NewEntries = erlang:make_tuple(NewSize, undefined), - %% Copy existing entries - NewEntries2 = copy_entries(Entries, NewEntries, 1, OldSize), - {NewEntries2, NewSize}. - -copy_entries(_Old, New, I, Size) when I > Size -> - New; -copy_entries(Old, New, I, Size) -> - copy_entries(Old, setelement(I, New, element(I, Old)), I + 1, Size). - -%% Evict oldest entries to make room -evict_entries(State = #state{max_size = MaxSize}, NewEntrySize) -> - TargetSize = MaxSize - NewEntrySize, - evict_until(State, TargetSize). - -evict_until(State = #state{size = Size}, TargetSize) when Size =< TargetSize -> - State; -evict_until(State = #state{count = 0}, _TargetSize) -> - State; -evict_until(State = #state{ - size = Size, - entries = Entries, - head = Head, - count = Count, - field_index = FieldIndex, - name_index = NameIndex -}, TargetSize) -> - %% Find oldest entry (tail of ring) - RingSize = tuple_size(Entries), - TailPos = ((Head - Count + RingSize) rem RingSize) + 1, - {EntrySize, {Name, _Value} = Entry} = element(TailPos, Entries), - %% Remove from maps (only if pointing to this entry) - NewFieldIndex = maps:remove(Entry, FieldIndex), - NewNameIndex = maps:remove(Name, NameIndex), - %% Clear entry and update state - NewState = State#state{ - size = Size - EntrySize, - entries = setelement(TailPos, Entries, undefined), - count = Count - 1, - field_index = NewFieldIndex, - name_index = NewNameIndex - }, - evict_until(NewState, TargetSize). - -%% Update table size -table_update_size(0, State) -> - State#state{ - size = 0, - max_size = 0, - head = 0, - count = 0, - field_index = #{}, - name_index = #{} - }; -table_update_size(MaxSize, State = #state{size = CurrentSize}) when CurrentSize =< MaxSize -> - State#state{max_size = MaxSize}; -table_update_size(MaxSize, State) -> - %% Need to evict entries - evict_until(State#state{max_size = MaxSize}, MaxSize). diff --git a/src/hackney_hpack_huffman.hrl b/src/hackney_hpack_huffman.hrl deleted file mode 100644 index f9ebbe2f..00000000 --- a/src/hackney_hpack_huffman.hrl +++ /dev/null @@ -1,330 +0,0 @@ -%%% -*- erlang -*- -%%% -%%% This file is part of hackney released under the Apache 2 license. -%%% See the NOTICE for more information. -%%% -%%% Copyright (c) 2024-2026 Benoit Chesneau -%%% -%%% @doc HPACK Huffman encoding lookup tables for O(1) encoding. -%%% -%%% RFC 7541 Appendix B defines the Huffman code table. -%%% This file provides pre-computed lookup for fast encoding. -%%% @end - -%% Huffman encoding table: {Code, BitLength} -%% Each entry is {huffman code bits, number of bits} -%% The codes are stored MSB-first as integers. - --define(HUFFMAN_ENCODE_TABLE, { - %% 0-15 (control characters) - {16#1ff8, 13}, %% 0 - {16#7fffd8, 23}, %% 1 - {16#fffffe2, 28}, %% 2 - {16#fffffe3, 28}, %% 3 - {16#fffffe4, 28}, %% 4 - {16#fffffe5, 28}, %% 5 - {16#fffffe6, 28}, %% 6 - {16#fffffe7, 28}, %% 7 - {16#fffffe8, 28}, %% 8 - {16#ffffea, 24}, %% 9 (TAB) - {16#3ffffffc, 30}, %% 10 (LF) - {16#fffffe9, 28}, %% 11 - {16#fffffea, 28}, %% 12 - {16#3ffffffd, 30}, %% 13 (CR) - {16#fffffeb, 28}, %% 14 - {16#fffffec, 28}, %% 15 - - %% 16-31 (more control characters) - {16#fffffed, 28}, %% 16 - {16#fffffee, 28}, %% 17 - {16#fffffef, 28}, %% 18 - {16#ffffff0, 28}, %% 19 - {16#ffffff1, 28}, %% 20 - {16#ffffff2, 28}, %% 21 - {16#3ffffffe, 30}, %% 22 - {16#ffffff3, 28}, %% 23 - {16#ffffff4, 28}, %% 24 - {16#ffffff5, 28}, %% 25 - {16#ffffff6, 28}, %% 26 - {16#ffffff7, 28}, %% 27 - {16#ffffff8, 28}, %% 28 - {16#ffffff9, 28}, %% 29 - {16#ffffffa, 28}, %% 30 - {16#ffffffb, 28}, %% 31 - - %% 32-47 (printable ASCII start) - {16#14, 6}, %% 32 ' ' - {16#3f8, 10}, %% 33 '!' - {16#3f9, 10}, %% 34 '"' - {16#ffa, 12}, %% 35 '#' - {16#1ff9, 13}, %% 36 '$' - {16#15, 6}, %% 37 '%' - {16#f8, 8}, %% 38 '&' - {16#7fa, 11}, %% 39 ''' - {16#3fa, 10}, %% 40 '(' - {16#3fb, 10}, %% 41 ')' - {16#f9, 8}, %% 42 '*' - {16#7fb, 11}, %% 43 '+' - {16#fa, 8}, %% 44 ',' - {16#16, 6}, %% 45 '-' - {16#17, 6}, %% 46 '.' - {16#18, 6}, %% 47 '/' - - %% 48-63 (digits and some symbols) - {16#0, 5}, %% 48 '0' - {16#1, 5}, %% 49 '1' - {16#2, 5}, %% 50 '2' - {16#19, 6}, %% 51 '3' - {16#1a, 6}, %% 52 '4' - {16#1b, 6}, %% 53 '5' - {16#1c, 6}, %% 54 '6' - {16#1d, 6}, %% 55 '7' - {16#1e, 6}, %% 56 '8' - {16#1f, 6}, %% 57 '9' - {16#5c, 7}, %% 58 ':' - {16#fb, 8}, %% 59 ';' - {16#7ffc, 15}, %% 60 '<' - {16#20, 6}, %% 61 '=' - {16#ffb, 12}, %% 62 '>' - {16#3fc, 10}, %% 63 '?' - - %% 64-79 (uppercase letters start) - {16#1ffa, 13}, %% 64 '@' - {16#21, 6}, %% 65 'A' - {16#5d, 7}, %% 66 'B' - {16#5e, 7}, %% 67 'C' - {16#5f, 7}, %% 68 'D' - {16#60, 7}, %% 69 'E' - {16#61, 7}, %% 70 'F' - {16#62, 7}, %% 71 'G' - {16#63, 7}, %% 72 'H' - {16#64, 7}, %% 73 'I' - {16#65, 7}, %% 74 'J' - {16#66, 7}, %% 75 'K' - {16#67, 7}, %% 76 'L' - {16#68, 7}, %% 77 'M' - {16#69, 7}, %% 78 'N' - {16#6a, 7}, %% 79 'O' - - %% 80-95 (uppercase letters continue) - {16#6b, 7}, %% 80 'P' - {16#6c, 7}, %% 81 'Q' - {16#6d, 7}, %% 82 'R' - {16#6e, 7}, %% 83 'S' - {16#6f, 7}, %% 84 'T' - {16#70, 7}, %% 85 'U' - {16#71, 7}, %% 86 'V' - {16#72, 7}, %% 87 'W' - {16#fc, 8}, %% 88 'X' - {16#73, 7}, %% 89 'Y' - {16#fd, 8}, %% 90 'Z' - {16#1ffb, 13}, %% 91 '[' - {16#7fff0, 19}, %% 92 '\' - {16#1ffc, 13}, %% 93 ']' - {16#3ffc, 14}, %% 94 '^' - {16#22, 6}, %% 95 '_' - - %% 96-111 (lowercase letters start) - {16#7ffd, 15}, %% 96 '`' - {16#3, 5}, %% 97 'a' - {16#23, 6}, %% 98 'b' - {16#4, 5}, %% 99 'c' - {16#24, 6}, %% 100 'd' - {16#5, 5}, %% 101 'e' - {16#25, 6}, %% 102 'f' - {16#26, 6}, %% 103 'g' - {16#27, 6}, %% 104 'h' - {16#6, 5}, %% 105 'i' - {16#74, 7}, %% 106 'j' - {16#75, 7}, %% 107 'k' - {16#28, 6}, %% 108 'l' - {16#29, 6}, %% 109 'm' - {16#2a, 6}, %% 110 'n' - {16#7, 5}, %% 111 'o' - - %% 112-127 (lowercase letters continue) - {16#2b, 6}, %% 112 'p' - {16#76, 7}, %% 113 'q' - {16#2c, 6}, %% 114 'r' - {16#8, 5}, %% 115 's' - {16#9, 5}, %% 116 't' - {16#2d, 6}, %% 117 'u' - {16#77, 7}, %% 118 'v' - {16#78, 7}, %% 119 'w' - {16#79, 7}, %% 120 'x' - {16#7a, 7}, %% 121 'y' - {16#7b, 7}, %% 122 'z' - {16#7ffe, 15}, %% 123 '{' - {16#7fc, 11}, %% 124 '|' - {16#3ffd, 14}, %% 125 '}' - {16#1ffd, 13}, %% 126 '~' - {16#ffffffc, 28}, %% 127 (DEL) - - %% 128-143 (extended ASCII) - {16#fffe6, 20}, %% 128 - {16#3fffd2, 22}, %% 129 - {16#fffe7, 20}, %% 130 - {16#fffe8, 20}, %% 131 - {16#3fffd3, 22}, %% 132 - {16#3fffd4, 22}, %% 133 - {16#3fffd5, 22}, %% 134 - {16#7fffd9, 23}, %% 135 - {16#3fffd6, 22}, %% 136 - {16#7fffda, 23}, %% 137 - {16#7fffdb, 23}, %% 138 - {16#7fffdc, 23}, %% 139 - {16#7fffdd, 23}, %% 140 - {16#7fffde, 23}, %% 141 - {16#ffffeb, 24}, %% 142 - {16#7fffdf, 23}, %% 143 - - %% 144-159 - {16#ffffec, 24}, %% 144 - {16#ffffed, 24}, %% 145 - {16#3fffd7, 22}, %% 146 - {16#7fffe0, 23}, %% 147 - {16#ffffee, 24}, %% 148 - {16#7fffe1, 23}, %% 149 - {16#7fffe2, 23}, %% 150 - {16#7fffe3, 23}, %% 151 - {16#7fffe4, 23}, %% 152 - {16#1fffdc, 21}, %% 153 - {16#3fffd8, 22}, %% 154 - {16#7fffe5, 23}, %% 155 - {16#3fffd9, 22}, %% 156 - {16#7fffe6, 23}, %% 157 - {16#7fffe7, 23}, %% 158 - {16#ffffef, 24}, %% 159 - - %% 160-175 - {16#3fffda, 22}, %% 160 - {16#1fffdd, 21}, %% 161 - {16#fffe9, 20}, %% 162 - {16#3fffdb, 22}, %% 163 - {16#3fffdc, 22}, %% 164 - {16#7fffe8, 23}, %% 165 - {16#7fffe9, 23}, %% 166 - {16#1fffde, 21}, %% 167 - {16#7fffea, 23}, %% 168 - {16#3fffdd, 22}, %% 169 - {16#3fffde, 22}, %% 170 - {16#fffff0, 24}, %% 171 - {16#1fffdf, 21}, %% 172 - {16#3fffdf, 22}, %% 173 - {16#7fffeb, 23}, %% 174 - {16#7fffec, 23}, %% 175 - - %% 176-191 - {16#1fffe0, 21}, %% 176 - {16#1fffe1, 21}, %% 177 - {16#3fffe0, 22}, %% 178 - {16#1fffe2, 21}, %% 179 - {16#7fffed, 23}, %% 180 - {16#3fffe1, 22}, %% 181 - {16#7fffee, 23}, %% 182 - {16#7fffef, 23}, %% 183 - {16#fffea, 20}, %% 184 - {16#3fffe2, 22}, %% 185 - {16#3fffe3, 22}, %% 186 - {16#3fffe4, 22}, %% 187 - {16#7ffff0, 23}, %% 188 - {16#3fffe5, 22}, %% 189 - {16#3fffe6, 22}, %% 190 - {16#7ffff1, 23}, %% 191 - - %% 192-207 - {16#3ffffe0, 26}, %% 192 - {16#3ffffe1, 26}, %% 193 - {16#fffeb, 20}, %% 194 - {16#7fff1, 19}, %% 195 - {16#3fffe7, 22}, %% 196 - {16#7ffff2, 23}, %% 197 - {16#3fffe8, 22}, %% 198 - {16#1ffffec, 25}, %% 199 - {16#3ffffe2, 26}, %% 200 - {16#3ffffe3, 26}, %% 201 - {16#3ffffe4, 26}, %% 202 - {16#7ffffde, 27}, %% 203 - {16#7ffffdf, 27}, %% 204 - {16#3ffffe5, 26}, %% 205 - {16#fffff1, 24}, %% 206 - {16#1ffffed, 25}, %% 207 - - %% 208-223 - {16#7fff2, 19}, %% 208 - {16#1fffe3, 21}, %% 209 - {16#3ffffe6, 26}, %% 210 - {16#7ffffe0, 27}, %% 211 - {16#7ffffe1, 27}, %% 212 - {16#3ffffe7, 26}, %% 213 - {16#7ffffe2, 27}, %% 214 - {16#fffff2, 24}, %% 215 - {16#1fffe4, 21}, %% 216 - {16#1fffe5, 21}, %% 217 - {16#3ffffe8, 26}, %% 218 - {16#3ffffe9, 26}, %% 219 - {16#ffffffd, 28}, %% 220 - {16#7ffffe3, 27}, %% 221 - {16#7ffffe4, 27}, %% 222 - {16#7ffffe5, 27}, %% 223 - - %% 224-239 - {16#fffec, 20}, %% 224 - {16#fffff3, 24}, %% 225 - {16#fffed, 20}, %% 226 - {16#1fffe6, 21}, %% 227 - {16#3fffe9, 22}, %% 228 - {16#1fffe7, 21}, %% 229 - {16#1fffe8, 21}, %% 230 - {16#7ffff3, 23}, %% 231 - {16#3fffea, 22}, %% 232 - {16#3fffeb, 22}, %% 233 - {16#1ffffee, 25}, %% 234 - {16#1ffffef, 25}, %% 235 - {16#fffff4, 24}, %% 236 - {16#fffff5, 24}, %% 237 - {16#3ffffea, 26}, %% 238 - {16#7ffff4, 23}, %% 239 - - %% 240-255 - {16#3ffffeb, 26}, %% 240 - {16#7ffffe6, 27}, %% 241 - {16#3ffffec, 26}, %% 242 - {16#3ffffed, 26}, %% 243 - {16#7ffffe7, 27}, %% 244 - {16#7ffffe8, 27}, %% 245 - {16#7ffffe9, 27}, %% 246 - {16#7ffffea, 27}, %% 247 - {16#7ffffeb, 27}, %% 248 - {16#ffffffe, 28}, %% 249 - {16#7ffffec, 27}, %% 250 - {16#7ffffed, 27}, %% 251 - {16#7ffffee, 27}, %% 252 - {16#7ffffef, 27}, %% 253 - {16#7fffff0, 27}, %% 254 - {16#3ffffee, 26} %% 255 -}). - -%% EOS symbol (256) used for padding: code = 0x3fffffff, len = 30 --define(HUFFMAN_EOS_CODE, 16#3fffffff). --define(HUFFMAN_EOS_BITS, 30). - -%% Pre-encoded common header values for O(1) access -%% These are the Huffman-encoded forms of common values - -%% Common methods (pre-encoded with huffman) --define(HUFFMAN_GET, <<16#c1>>). %% "GET" huffman encoded --define(HUFFMAN_POST, <<16#e1, 16#bf>>). %% "POST" huffman encoded - -%% Common status codes (pre-encoded with huffman) --define(HUFFMAN_200, <<16#08>>). %% "200" huffman encoded --define(HUFFMAN_204, <<16#09, 16#a2>>). %% "204" huffman encoded --define(HUFFMAN_206, <<16#09, 16#c2>>). %% "206" huffman encoded --define(HUFFMAN_304, <<16#1a, 16#62>>). %% "304" huffman encoded --define(HUFFMAN_400, <<16#4c, 16#08>>). %% "400" huffman encoded --define(HUFFMAN_404, <<16#4c, 16#62>>). %% "404" huffman encoded --define(HUFFMAN_500, <<16#a2, 16#08>>). %% "500" huffman encoded - -%% Macro to get huffman code for a byte value (0-255) -%% Returns {Code, BitLength} --define(HUFFMAN_CODE(Byte), element(Byte + 1, ?HUFFMAN_ENCODE_TABLE)). diff --git a/src/hackney_hpack_huffman_dec.hrl b/src/hackney_hpack_huffman_dec.hrl deleted file mode 100644 index 6c37c685..00000000 --- a/src/hackney_hpack_huffman_dec.hrl +++ /dev/null @@ -1,4137 +0,0 @@ -%%% -*- erlang -*- -%%% -%%% Vendored from cowlib (https://github.com/ninenines/cowlib) -%%% Original file: cow_hpack_dec_huffman_lookup.hrl -%%% -%% Copyright (c) 2019-2023, Loïc Hoguin -%% -%% Permission to use, copy, modify, and/or distribute this software for any -%% purpose with or without fee is hereby granted, provided that the above -%% copyright notice and this permission notice appear in all copies. -%% -%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -%% This lookup function was created by converting the -%% table from Nginx[1] into a form better suitable for -%% Erlang/OTP. This particular table takes a byte-sized -%% state and 4 bits to determine whether to emit a -%% character and what the next state is. It is most -%% appropriate for Erlang/OTP because we can benefit -%% from binary pattern matching optimizations by -%% matching the binary one byte at a time, calling -%% this lookup function twice. This and similar -%% algorithms are discussed here[2] and there[3]. -%% -%% It is possible to write a lookup table taking -%% a full byte instead of just 4 bits, but this -%% would make this function take 65536 clauses instead -%% of the current 4096. This could be done later -%% as a further optimization but might not yield -%% significant improvements. -%% -%% [1] https://hg.nginx.org/nginx/file/tip/src/http/v2/ngx_http_v2_huff_decode.c -%% [2] http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.9.4248&rep=rep1&type=pdf -%% [3] https://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art007 - -dec_huffman_lookup(16#00, 16#0) -> {more, undefined, 16#04}; -dec_huffman_lookup(16#00, 16#1) -> {more, undefined, 16#05}; -dec_huffman_lookup(16#00, 16#2) -> {more, undefined, 16#07}; -dec_huffman_lookup(16#00, 16#3) -> {more, undefined, 16#08}; -dec_huffman_lookup(16#00, 16#4) -> {more, undefined, 16#0b}; -dec_huffman_lookup(16#00, 16#5) -> {more, undefined, 16#0c}; -dec_huffman_lookup(16#00, 16#6) -> {more, undefined, 16#10}; -dec_huffman_lookup(16#00, 16#7) -> {more, undefined, 16#13}; -dec_huffman_lookup(16#00, 16#8) -> {more, undefined, 16#19}; -dec_huffman_lookup(16#00, 16#9) -> {more, undefined, 16#1c}; -dec_huffman_lookup(16#00, 16#a) -> {more, undefined, 16#20}; -dec_huffman_lookup(16#00, 16#b) -> {more, undefined, 16#23}; -dec_huffman_lookup(16#00, 16#c) -> {more, undefined, 16#2a}; -dec_huffman_lookup(16#00, 16#d) -> {more, undefined, 16#31}; -dec_huffman_lookup(16#00, 16#e) -> {more, undefined, 16#39}; -dec_huffman_lookup(16#00, 16#f) -> {ok, undefined, 16#40}; -dec_huffman_lookup(16#01, 16#0) -> {ok, 16#30, 16#00}; -dec_huffman_lookup(16#01, 16#1) -> {ok, 16#31, 16#00}; -dec_huffman_lookup(16#01, 16#2) -> {ok, 16#32, 16#00}; -dec_huffman_lookup(16#01, 16#3) -> {ok, 16#61, 16#00}; -dec_huffman_lookup(16#01, 16#4) -> {ok, 16#63, 16#00}; -dec_huffman_lookup(16#01, 16#5) -> {ok, 16#65, 16#00}; -dec_huffman_lookup(16#01, 16#6) -> {ok, 16#69, 16#00}; -dec_huffman_lookup(16#01, 16#7) -> {ok, 16#6f, 16#00}; -dec_huffman_lookup(16#01, 16#8) -> {ok, 16#73, 16#00}; -dec_huffman_lookup(16#01, 16#9) -> {ok, 16#74, 16#00}; -dec_huffman_lookup(16#01, 16#a) -> {more, undefined, 16#0d}; -dec_huffman_lookup(16#01, 16#b) -> {more, undefined, 16#0e}; -dec_huffman_lookup(16#01, 16#c) -> {more, undefined, 16#11}; -dec_huffman_lookup(16#01, 16#d) -> {more, undefined, 16#12}; -dec_huffman_lookup(16#01, 16#e) -> {more, undefined, 16#14}; -dec_huffman_lookup(16#01, 16#f) -> {more, undefined, 16#15}; -dec_huffman_lookup(16#02, 16#0) -> {more, 16#30, 16#01}; -dec_huffman_lookup(16#02, 16#1) -> {ok, 16#30, 16#16}; -dec_huffman_lookup(16#02, 16#2) -> {more, 16#31, 16#01}; -dec_huffman_lookup(16#02, 16#3) -> {ok, 16#31, 16#16}; -dec_huffman_lookup(16#02, 16#4) -> {more, 16#32, 16#01}; -dec_huffman_lookup(16#02, 16#5) -> {ok, 16#32, 16#16}; -dec_huffman_lookup(16#02, 16#6) -> {more, 16#61, 16#01}; -dec_huffman_lookup(16#02, 16#7) -> {ok, 16#61, 16#16}; -dec_huffman_lookup(16#02, 16#8) -> {more, 16#63, 16#01}; -dec_huffman_lookup(16#02, 16#9) -> {ok, 16#63, 16#16}; -dec_huffman_lookup(16#02, 16#a) -> {more, 16#65, 16#01}; -dec_huffman_lookup(16#02, 16#b) -> {ok, 16#65, 16#16}; -dec_huffman_lookup(16#02, 16#c) -> {more, 16#69, 16#01}; -dec_huffman_lookup(16#02, 16#d) -> {ok, 16#69, 16#16}; -dec_huffman_lookup(16#02, 16#e) -> {more, 16#6f, 16#01}; -dec_huffman_lookup(16#02, 16#f) -> {ok, 16#6f, 16#16}; -dec_huffman_lookup(16#03, 16#0) -> {more, 16#30, 16#02}; -dec_huffman_lookup(16#03, 16#1) -> {more, 16#30, 16#09}; -dec_huffman_lookup(16#03, 16#2) -> {more, 16#30, 16#17}; -dec_huffman_lookup(16#03, 16#3) -> {ok, 16#30, 16#28}; -dec_huffman_lookup(16#03, 16#4) -> {more, 16#31, 16#02}; -dec_huffman_lookup(16#03, 16#5) -> {more, 16#31, 16#09}; -dec_huffman_lookup(16#03, 16#6) -> {more, 16#31, 16#17}; -dec_huffman_lookup(16#03, 16#7) -> {ok, 16#31, 16#28}; -dec_huffman_lookup(16#03, 16#8) -> {more, 16#32, 16#02}; -dec_huffman_lookup(16#03, 16#9) -> {more, 16#32, 16#09}; -dec_huffman_lookup(16#03, 16#a) -> {more, 16#32, 16#17}; -dec_huffman_lookup(16#03, 16#b) -> {ok, 16#32, 16#28}; -dec_huffman_lookup(16#03, 16#c) -> {more, 16#61, 16#02}; -dec_huffman_lookup(16#03, 16#d) -> {more, 16#61, 16#09}; -dec_huffman_lookup(16#03, 16#e) -> {more, 16#61, 16#17}; -dec_huffman_lookup(16#03, 16#f) -> {ok, 16#61, 16#28}; -dec_huffman_lookup(16#04, 16#0) -> {more, 16#30, 16#03}; -dec_huffman_lookup(16#04, 16#1) -> {more, 16#30, 16#06}; -dec_huffman_lookup(16#04, 16#2) -> {more, 16#30, 16#0a}; -dec_huffman_lookup(16#04, 16#3) -> {more, 16#30, 16#0f}; -dec_huffman_lookup(16#04, 16#4) -> {more, 16#30, 16#18}; -dec_huffman_lookup(16#04, 16#5) -> {more, 16#30, 16#1f}; -dec_huffman_lookup(16#04, 16#6) -> {more, 16#30, 16#29}; -dec_huffman_lookup(16#04, 16#7) -> {ok, 16#30, 16#38}; -dec_huffman_lookup(16#04, 16#8) -> {more, 16#31, 16#03}; -dec_huffman_lookup(16#04, 16#9) -> {more, 16#31, 16#06}; -dec_huffman_lookup(16#04, 16#a) -> {more, 16#31, 16#0a}; -dec_huffman_lookup(16#04, 16#b) -> {more, 16#31, 16#0f}; -dec_huffman_lookup(16#04, 16#c) -> {more, 16#31, 16#18}; -dec_huffman_lookup(16#04, 16#d) -> {more, 16#31, 16#1f}; -dec_huffman_lookup(16#04, 16#e) -> {more, 16#31, 16#29}; -dec_huffman_lookup(16#04, 16#f) -> {ok, 16#31, 16#38}; -dec_huffman_lookup(16#05, 16#0) -> {more, 16#32, 16#03}; -dec_huffman_lookup(16#05, 16#1) -> {more, 16#32, 16#06}; -dec_huffman_lookup(16#05, 16#2) -> {more, 16#32, 16#0a}; -dec_huffman_lookup(16#05, 16#3) -> {more, 16#32, 16#0f}; -dec_huffman_lookup(16#05, 16#4) -> {more, 16#32, 16#18}; -dec_huffman_lookup(16#05, 16#5) -> {more, 16#32, 16#1f}; -dec_huffman_lookup(16#05, 16#6) -> {more, 16#32, 16#29}; -dec_huffman_lookup(16#05, 16#7) -> {ok, 16#32, 16#38}; -dec_huffman_lookup(16#05, 16#8) -> {more, 16#61, 16#03}; -dec_huffman_lookup(16#05, 16#9) -> {more, 16#61, 16#06}; -dec_huffman_lookup(16#05, 16#a) -> {more, 16#61, 16#0a}; -dec_huffman_lookup(16#05, 16#b) -> {more, 16#61, 16#0f}; -dec_huffman_lookup(16#05, 16#c) -> {more, 16#61, 16#18}; -dec_huffman_lookup(16#05, 16#d) -> {more, 16#61, 16#1f}; -dec_huffman_lookup(16#05, 16#e) -> {more, 16#61, 16#29}; -dec_huffman_lookup(16#05, 16#f) -> {ok, 16#61, 16#38}; -dec_huffman_lookup(16#06, 16#0) -> {more, 16#63, 16#02}; -dec_huffman_lookup(16#06, 16#1) -> {more, 16#63, 16#09}; -dec_huffman_lookup(16#06, 16#2) -> {more, 16#63, 16#17}; -dec_huffman_lookup(16#06, 16#3) -> {ok, 16#63, 16#28}; -dec_huffman_lookup(16#06, 16#4) -> {more, 16#65, 16#02}; -dec_huffman_lookup(16#06, 16#5) -> {more, 16#65, 16#09}; -dec_huffman_lookup(16#06, 16#6) -> {more, 16#65, 16#17}; -dec_huffman_lookup(16#06, 16#7) -> {ok, 16#65, 16#28}; -dec_huffman_lookup(16#06, 16#8) -> {more, 16#69, 16#02}; -dec_huffman_lookup(16#06, 16#9) -> {more, 16#69, 16#09}; -dec_huffman_lookup(16#06, 16#a) -> {more, 16#69, 16#17}; -dec_huffman_lookup(16#06, 16#b) -> {ok, 16#69, 16#28}; -dec_huffman_lookup(16#06, 16#c) -> {more, 16#6f, 16#02}; -dec_huffman_lookup(16#06, 16#d) -> {more, 16#6f, 16#09}; -dec_huffman_lookup(16#06, 16#e) -> {more, 16#6f, 16#17}; -dec_huffman_lookup(16#06, 16#f) -> {ok, 16#6f, 16#28}; -dec_huffman_lookup(16#07, 16#0) -> {more, 16#63, 16#03}; -dec_huffman_lookup(16#07, 16#1) -> {more, 16#63, 16#06}; -dec_huffman_lookup(16#07, 16#2) -> {more, 16#63, 16#0a}; -dec_huffman_lookup(16#07, 16#3) -> {more, 16#63, 16#0f}; -dec_huffman_lookup(16#07, 16#4) -> {more, 16#63, 16#18}; -dec_huffman_lookup(16#07, 16#5) -> {more, 16#63, 16#1f}; -dec_huffman_lookup(16#07, 16#6) -> {more, 16#63, 16#29}; -dec_huffman_lookup(16#07, 16#7) -> {ok, 16#63, 16#38}; -dec_huffman_lookup(16#07, 16#8) -> {more, 16#65, 16#03}; -dec_huffman_lookup(16#07, 16#9) -> {more, 16#65, 16#06}; -dec_huffman_lookup(16#07, 16#a) -> {more, 16#65, 16#0a}; -dec_huffman_lookup(16#07, 16#b) -> {more, 16#65, 16#0f}; -dec_huffman_lookup(16#07, 16#c) -> {more, 16#65, 16#18}; -dec_huffman_lookup(16#07, 16#d) -> {more, 16#65, 16#1f}; -dec_huffman_lookup(16#07, 16#e) -> {more, 16#65, 16#29}; -dec_huffman_lookup(16#07, 16#f) -> {ok, 16#65, 16#38}; -dec_huffman_lookup(16#08, 16#0) -> {more, 16#69, 16#03}; -dec_huffman_lookup(16#08, 16#1) -> {more, 16#69, 16#06}; -dec_huffman_lookup(16#08, 16#2) -> {more, 16#69, 16#0a}; -dec_huffman_lookup(16#08, 16#3) -> {more, 16#69, 16#0f}; -dec_huffman_lookup(16#08, 16#4) -> {more, 16#69, 16#18}; -dec_huffman_lookup(16#08, 16#5) -> {more, 16#69, 16#1f}; -dec_huffman_lookup(16#08, 16#6) -> {more, 16#69, 16#29}; -dec_huffman_lookup(16#08, 16#7) -> {ok, 16#69, 16#38}; -dec_huffman_lookup(16#08, 16#8) -> {more, 16#6f, 16#03}; -dec_huffman_lookup(16#08, 16#9) -> {more, 16#6f, 16#06}; -dec_huffman_lookup(16#08, 16#a) -> {more, 16#6f, 16#0a}; -dec_huffman_lookup(16#08, 16#b) -> {more, 16#6f, 16#0f}; -dec_huffman_lookup(16#08, 16#c) -> {more, 16#6f, 16#18}; -dec_huffman_lookup(16#08, 16#d) -> {more, 16#6f, 16#1f}; -dec_huffman_lookup(16#08, 16#e) -> {more, 16#6f, 16#29}; -dec_huffman_lookup(16#08, 16#f) -> {ok, 16#6f, 16#38}; -dec_huffman_lookup(16#09, 16#0) -> {more, 16#73, 16#01}; -dec_huffman_lookup(16#09, 16#1) -> {ok, 16#73, 16#16}; -dec_huffman_lookup(16#09, 16#2) -> {more, 16#74, 16#01}; -dec_huffman_lookup(16#09, 16#3) -> {ok, 16#74, 16#16}; -dec_huffman_lookup(16#09, 16#4) -> {ok, 16#20, 16#00}; -dec_huffman_lookup(16#09, 16#5) -> {ok, 16#25, 16#00}; -dec_huffman_lookup(16#09, 16#6) -> {ok, 16#2d, 16#00}; -dec_huffman_lookup(16#09, 16#7) -> {ok, 16#2e, 16#00}; -dec_huffman_lookup(16#09, 16#8) -> {ok, 16#2f, 16#00}; -dec_huffman_lookup(16#09, 16#9) -> {ok, 16#33, 16#00}; -dec_huffman_lookup(16#09, 16#a) -> {ok, 16#34, 16#00}; -dec_huffman_lookup(16#09, 16#b) -> {ok, 16#35, 16#00}; -dec_huffman_lookup(16#09, 16#c) -> {ok, 16#36, 16#00}; -dec_huffman_lookup(16#09, 16#d) -> {ok, 16#37, 16#00}; -dec_huffman_lookup(16#09, 16#e) -> {ok, 16#38, 16#00}; -dec_huffman_lookup(16#09, 16#f) -> {ok, 16#39, 16#00}; -dec_huffman_lookup(16#0a, 16#0) -> {more, 16#73, 16#02}; -dec_huffman_lookup(16#0a, 16#1) -> {more, 16#73, 16#09}; -dec_huffman_lookup(16#0a, 16#2) -> {more, 16#73, 16#17}; -dec_huffman_lookup(16#0a, 16#3) -> {ok, 16#73, 16#28}; -dec_huffman_lookup(16#0a, 16#4) -> {more, 16#74, 16#02}; -dec_huffman_lookup(16#0a, 16#5) -> {more, 16#74, 16#09}; -dec_huffman_lookup(16#0a, 16#6) -> {more, 16#74, 16#17}; -dec_huffman_lookup(16#0a, 16#7) -> {ok, 16#74, 16#28}; -dec_huffman_lookup(16#0a, 16#8) -> {more, 16#20, 16#01}; -dec_huffman_lookup(16#0a, 16#9) -> {ok, 16#20, 16#16}; -dec_huffman_lookup(16#0a, 16#a) -> {more, 16#25, 16#01}; -dec_huffman_lookup(16#0a, 16#b) -> {ok, 16#25, 16#16}; -dec_huffman_lookup(16#0a, 16#c) -> {more, 16#2d, 16#01}; -dec_huffman_lookup(16#0a, 16#d) -> {ok, 16#2d, 16#16}; -dec_huffman_lookup(16#0a, 16#e) -> {more, 16#2e, 16#01}; -dec_huffman_lookup(16#0a, 16#f) -> {ok, 16#2e, 16#16}; -dec_huffman_lookup(16#0b, 16#0) -> {more, 16#73, 16#03}; -dec_huffman_lookup(16#0b, 16#1) -> {more, 16#73, 16#06}; -dec_huffman_lookup(16#0b, 16#2) -> {more, 16#73, 16#0a}; -dec_huffman_lookup(16#0b, 16#3) -> {more, 16#73, 16#0f}; -dec_huffman_lookup(16#0b, 16#4) -> {more, 16#73, 16#18}; -dec_huffman_lookup(16#0b, 16#5) -> {more, 16#73, 16#1f}; -dec_huffman_lookup(16#0b, 16#6) -> {more, 16#73, 16#29}; -dec_huffman_lookup(16#0b, 16#7) -> {ok, 16#73, 16#38}; -dec_huffman_lookup(16#0b, 16#8) -> {more, 16#74, 16#03}; -dec_huffman_lookup(16#0b, 16#9) -> {more, 16#74, 16#06}; -dec_huffman_lookup(16#0b, 16#a) -> {more, 16#74, 16#0a}; -dec_huffman_lookup(16#0b, 16#b) -> {more, 16#74, 16#0f}; -dec_huffman_lookup(16#0b, 16#c) -> {more, 16#74, 16#18}; -dec_huffman_lookup(16#0b, 16#d) -> {more, 16#74, 16#1f}; -dec_huffman_lookup(16#0b, 16#e) -> {more, 16#74, 16#29}; -dec_huffman_lookup(16#0b, 16#f) -> {ok, 16#74, 16#38}; -dec_huffman_lookup(16#0c, 16#0) -> {more, 16#20, 16#02}; -dec_huffman_lookup(16#0c, 16#1) -> {more, 16#20, 16#09}; -dec_huffman_lookup(16#0c, 16#2) -> {more, 16#20, 16#17}; -dec_huffman_lookup(16#0c, 16#3) -> {ok, 16#20, 16#28}; -dec_huffman_lookup(16#0c, 16#4) -> {more, 16#25, 16#02}; -dec_huffman_lookup(16#0c, 16#5) -> {more, 16#25, 16#09}; -dec_huffman_lookup(16#0c, 16#6) -> {more, 16#25, 16#17}; -dec_huffman_lookup(16#0c, 16#7) -> {ok, 16#25, 16#28}; -dec_huffman_lookup(16#0c, 16#8) -> {more, 16#2d, 16#02}; -dec_huffman_lookup(16#0c, 16#9) -> {more, 16#2d, 16#09}; -dec_huffman_lookup(16#0c, 16#a) -> {more, 16#2d, 16#17}; -dec_huffman_lookup(16#0c, 16#b) -> {ok, 16#2d, 16#28}; -dec_huffman_lookup(16#0c, 16#c) -> {more, 16#2e, 16#02}; -dec_huffman_lookup(16#0c, 16#d) -> {more, 16#2e, 16#09}; -dec_huffman_lookup(16#0c, 16#e) -> {more, 16#2e, 16#17}; -dec_huffman_lookup(16#0c, 16#f) -> {ok, 16#2e, 16#28}; -dec_huffman_lookup(16#0d, 16#0) -> {more, 16#20, 16#03}; -dec_huffman_lookup(16#0d, 16#1) -> {more, 16#20, 16#06}; -dec_huffman_lookup(16#0d, 16#2) -> {more, 16#20, 16#0a}; -dec_huffman_lookup(16#0d, 16#3) -> {more, 16#20, 16#0f}; -dec_huffman_lookup(16#0d, 16#4) -> {more, 16#20, 16#18}; -dec_huffman_lookup(16#0d, 16#5) -> {more, 16#20, 16#1f}; -dec_huffman_lookup(16#0d, 16#6) -> {more, 16#20, 16#29}; -dec_huffman_lookup(16#0d, 16#7) -> {ok, 16#20, 16#38}; -dec_huffman_lookup(16#0d, 16#8) -> {more, 16#25, 16#03}; -dec_huffman_lookup(16#0d, 16#9) -> {more, 16#25, 16#06}; -dec_huffman_lookup(16#0d, 16#a) -> {more, 16#25, 16#0a}; -dec_huffman_lookup(16#0d, 16#b) -> {more, 16#25, 16#0f}; -dec_huffman_lookup(16#0d, 16#c) -> {more, 16#25, 16#18}; -dec_huffman_lookup(16#0d, 16#d) -> {more, 16#25, 16#1f}; -dec_huffman_lookup(16#0d, 16#e) -> {more, 16#25, 16#29}; -dec_huffman_lookup(16#0d, 16#f) -> {ok, 16#25, 16#38}; -dec_huffman_lookup(16#0e, 16#0) -> {more, 16#2d, 16#03}; -dec_huffman_lookup(16#0e, 16#1) -> {more, 16#2d, 16#06}; -dec_huffman_lookup(16#0e, 16#2) -> {more, 16#2d, 16#0a}; -dec_huffman_lookup(16#0e, 16#3) -> {more, 16#2d, 16#0f}; -dec_huffman_lookup(16#0e, 16#4) -> {more, 16#2d, 16#18}; -dec_huffman_lookup(16#0e, 16#5) -> {more, 16#2d, 16#1f}; -dec_huffman_lookup(16#0e, 16#6) -> {more, 16#2d, 16#29}; -dec_huffman_lookup(16#0e, 16#7) -> {ok, 16#2d, 16#38}; -dec_huffman_lookup(16#0e, 16#8) -> {more, 16#2e, 16#03}; -dec_huffman_lookup(16#0e, 16#9) -> {more, 16#2e, 16#06}; -dec_huffman_lookup(16#0e, 16#a) -> {more, 16#2e, 16#0a}; -dec_huffman_lookup(16#0e, 16#b) -> {more, 16#2e, 16#0f}; -dec_huffman_lookup(16#0e, 16#c) -> {more, 16#2e, 16#18}; -dec_huffman_lookup(16#0e, 16#d) -> {more, 16#2e, 16#1f}; -dec_huffman_lookup(16#0e, 16#e) -> {more, 16#2e, 16#29}; -dec_huffman_lookup(16#0e, 16#f) -> {ok, 16#2e, 16#38}; -dec_huffman_lookup(16#0f, 16#0) -> {more, 16#2f, 16#01}; -dec_huffman_lookup(16#0f, 16#1) -> {ok, 16#2f, 16#16}; -dec_huffman_lookup(16#0f, 16#2) -> {more, 16#33, 16#01}; -dec_huffman_lookup(16#0f, 16#3) -> {ok, 16#33, 16#16}; -dec_huffman_lookup(16#0f, 16#4) -> {more, 16#34, 16#01}; -dec_huffman_lookup(16#0f, 16#5) -> {ok, 16#34, 16#16}; -dec_huffman_lookup(16#0f, 16#6) -> {more, 16#35, 16#01}; -dec_huffman_lookup(16#0f, 16#7) -> {ok, 16#35, 16#16}; -dec_huffman_lookup(16#0f, 16#8) -> {more, 16#36, 16#01}; -dec_huffman_lookup(16#0f, 16#9) -> {ok, 16#36, 16#16}; -dec_huffman_lookup(16#0f, 16#a) -> {more, 16#37, 16#01}; -dec_huffman_lookup(16#0f, 16#b) -> {ok, 16#37, 16#16}; -dec_huffman_lookup(16#0f, 16#c) -> {more, 16#38, 16#01}; -dec_huffman_lookup(16#0f, 16#d) -> {ok, 16#38, 16#16}; -dec_huffman_lookup(16#0f, 16#e) -> {more, 16#39, 16#01}; -dec_huffman_lookup(16#0f, 16#f) -> {ok, 16#39, 16#16}; -dec_huffman_lookup(16#10, 16#0) -> {more, 16#2f, 16#02}; -dec_huffman_lookup(16#10, 16#1) -> {more, 16#2f, 16#09}; -dec_huffman_lookup(16#10, 16#2) -> {more, 16#2f, 16#17}; -dec_huffman_lookup(16#10, 16#3) -> {ok, 16#2f, 16#28}; -dec_huffman_lookup(16#10, 16#4) -> {more, 16#33, 16#02}; -dec_huffman_lookup(16#10, 16#5) -> {more, 16#33, 16#09}; -dec_huffman_lookup(16#10, 16#6) -> {more, 16#33, 16#17}; -dec_huffman_lookup(16#10, 16#7) -> {ok, 16#33, 16#28}; -dec_huffman_lookup(16#10, 16#8) -> {more, 16#34, 16#02}; -dec_huffman_lookup(16#10, 16#9) -> {more, 16#34, 16#09}; -dec_huffman_lookup(16#10, 16#a) -> {more, 16#34, 16#17}; -dec_huffman_lookup(16#10, 16#b) -> {ok, 16#34, 16#28}; -dec_huffman_lookup(16#10, 16#c) -> {more, 16#35, 16#02}; -dec_huffman_lookup(16#10, 16#d) -> {more, 16#35, 16#09}; -dec_huffman_lookup(16#10, 16#e) -> {more, 16#35, 16#17}; -dec_huffman_lookup(16#10, 16#f) -> {ok, 16#35, 16#28}; -dec_huffman_lookup(16#11, 16#0) -> {more, 16#2f, 16#03}; -dec_huffman_lookup(16#11, 16#1) -> {more, 16#2f, 16#06}; -dec_huffman_lookup(16#11, 16#2) -> {more, 16#2f, 16#0a}; -dec_huffman_lookup(16#11, 16#3) -> {more, 16#2f, 16#0f}; -dec_huffman_lookup(16#11, 16#4) -> {more, 16#2f, 16#18}; -dec_huffman_lookup(16#11, 16#5) -> {more, 16#2f, 16#1f}; -dec_huffman_lookup(16#11, 16#6) -> {more, 16#2f, 16#29}; -dec_huffman_lookup(16#11, 16#7) -> {ok, 16#2f, 16#38}; -dec_huffman_lookup(16#11, 16#8) -> {more, 16#33, 16#03}; -dec_huffman_lookup(16#11, 16#9) -> {more, 16#33, 16#06}; -dec_huffman_lookup(16#11, 16#a) -> {more, 16#33, 16#0a}; -dec_huffman_lookup(16#11, 16#b) -> {more, 16#33, 16#0f}; -dec_huffman_lookup(16#11, 16#c) -> {more, 16#33, 16#18}; -dec_huffman_lookup(16#11, 16#d) -> {more, 16#33, 16#1f}; -dec_huffman_lookup(16#11, 16#e) -> {more, 16#33, 16#29}; -dec_huffman_lookup(16#11, 16#f) -> {ok, 16#33, 16#38}; -dec_huffman_lookup(16#12, 16#0) -> {more, 16#34, 16#03}; -dec_huffman_lookup(16#12, 16#1) -> {more, 16#34, 16#06}; -dec_huffman_lookup(16#12, 16#2) -> {more, 16#34, 16#0a}; -dec_huffman_lookup(16#12, 16#3) -> {more, 16#34, 16#0f}; -dec_huffman_lookup(16#12, 16#4) -> {more, 16#34, 16#18}; -dec_huffman_lookup(16#12, 16#5) -> {more, 16#34, 16#1f}; -dec_huffman_lookup(16#12, 16#6) -> {more, 16#34, 16#29}; -dec_huffman_lookup(16#12, 16#7) -> {ok, 16#34, 16#38}; -dec_huffman_lookup(16#12, 16#8) -> {more, 16#35, 16#03}; -dec_huffman_lookup(16#12, 16#9) -> {more, 16#35, 16#06}; -dec_huffman_lookup(16#12, 16#a) -> {more, 16#35, 16#0a}; -dec_huffman_lookup(16#12, 16#b) -> {more, 16#35, 16#0f}; -dec_huffman_lookup(16#12, 16#c) -> {more, 16#35, 16#18}; -dec_huffman_lookup(16#12, 16#d) -> {more, 16#35, 16#1f}; -dec_huffman_lookup(16#12, 16#e) -> {more, 16#35, 16#29}; -dec_huffman_lookup(16#12, 16#f) -> {ok, 16#35, 16#38}; -dec_huffman_lookup(16#13, 16#0) -> {more, 16#36, 16#02}; -dec_huffman_lookup(16#13, 16#1) -> {more, 16#36, 16#09}; -dec_huffman_lookup(16#13, 16#2) -> {more, 16#36, 16#17}; -dec_huffman_lookup(16#13, 16#3) -> {ok, 16#36, 16#28}; -dec_huffman_lookup(16#13, 16#4) -> {more, 16#37, 16#02}; -dec_huffman_lookup(16#13, 16#5) -> {more, 16#37, 16#09}; -dec_huffman_lookup(16#13, 16#6) -> {more, 16#37, 16#17}; -dec_huffman_lookup(16#13, 16#7) -> {ok, 16#37, 16#28}; -dec_huffman_lookup(16#13, 16#8) -> {more, 16#38, 16#02}; -dec_huffman_lookup(16#13, 16#9) -> {more, 16#38, 16#09}; -dec_huffman_lookup(16#13, 16#a) -> {more, 16#38, 16#17}; -dec_huffman_lookup(16#13, 16#b) -> {ok, 16#38, 16#28}; -dec_huffman_lookup(16#13, 16#c) -> {more, 16#39, 16#02}; -dec_huffman_lookup(16#13, 16#d) -> {more, 16#39, 16#09}; -dec_huffman_lookup(16#13, 16#e) -> {more, 16#39, 16#17}; -dec_huffman_lookup(16#13, 16#f) -> {ok, 16#39, 16#28}; -dec_huffman_lookup(16#14, 16#0) -> {more, 16#36, 16#03}; -dec_huffman_lookup(16#14, 16#1) -> {more, 16#36, 16#06}; -dec_huffman_lookup(16#14, 16#2) -> {more, 16#36, 16#0a}; -dec_huffman_lookup(16#14, 16#3) -> {more, 16#36, 16#0f}; -dec_huffman_lookup(16#14, 16#4) -> {more, 16#36, 16#18}; -dec_huffman_lookup(16#14, 16#5) -> {more, 16#36, 16#1f}; -dec_huffman_lookup(16#14, 16#6) -> {more, 16#36, 16#29}; -dec_huffman_lookup(16#14, 16#7) -> {ok, 16#36, 16#38}; -dec_huffman_lookup(16#14, 16#8) -> {more, 16#37, 16#03}; -dec_huffman_lookup(16#14, 16#9) -> {more, 16#37, 16#06}; -dec_huffman_lookup(16#14, 16#a) -> {more, 16#37, 16#0a}; -dec_huffman_lookup(16#14, 16#b) -> {more, 16#37, 16#0f}; -dec_huffman_lookup(16#14, 16#c) -> {more, 16#37, 16#18}; -dec_huffman_lookup(16#14, 16#d) -> {more, 16#37, 16#1f}; -dec_huffman_lookup(16#14, 16#e) -> {more, 16#37, 16#29}; -dec_huffman_lookup(16#14, 16#f) -> {ok, 16#37, 16#38}; -dec_huffman_lookup(16#15, 16#0) -> {more, 16#38, 16#03}; -dec_huffman_lookup(16#15, 16#1) -> {more, 16#38, 16#06}; -dec_huffman_lookup(16#15, 16#2) -> {more, 16#38, 16#0a}; -dec_huffman_lookup(16#15, 16#3) -> {more, 16#38, 16#0f}; -dec_huffman_lookup(16#15, 16#4) -> {more, 16#38, 16#18}; -dec_huffman_lookup(16#15, 16#5) -> {more, 16#38, 16#1f}; -dec_huffman_lookup(16#15, 16#6) -> {more, 16#38, 16#29}; -dec_huffman_lookup(16#15, 16#7) -> {ok, 16#38, 16#38}; -dec_huffman_lookup(16#15, 16#8) -> {more, 16#39, 16#03}; -dec_huffman_lookup(16#15, 16#9) -> {more, 16#39, 16#06}; -dec_huffman_lookup(16#15, 16#a) -> {more, 16#39, 16#0a}; -dec_huffman_lookup(16#15, 16#b) -> {more, 16#39, 16#0f}; -dec_huffman_lookup(16#15, 16#c) -> {more, 16#39, 16#18}; -dec_huffman_lookup(16#15, 16#d) -> {more, 16#39, 16#1f}; -dec_huffman_lookup(16#15, 16#e) -> {more, 16#39, 16#29}; -dec_huffman_lookup(16#15, 16#f) -> {ok, 16#39, 16#38}; -dec_huffman_lookup(16#16, 16#0) -> {more, undefined, 16#1a}; -dec_huffman_lookup(16#16, 16#1) -> {more, undefined, 16#1b}; -dec_huffman_lookup(16#16, 16#2) -> {more, undefined, 16#1d}; -dec_huffman_lookup(16#16, 16#3) -> {more, undefined, 16#1e}; -dec_huffman_lookup(16#16, 16#4) -> {more, undefined, 16#21}; -dec_huffman_lookup(16#16, 16#5) -> {more, undefined, 16#22}; -dec_huffman_lookup(16#16, 16#6) -> {more, undefined, 16#24}; -dec_huffman_lookup(16#16, 16#7) -> {more, undefined, 16#25}; -dec_huffman_lookup(16#16, 16#8) -> {more, undefined, 16#2b}; -dec_huffman_lookup(16#16, 16#9) -> {more, undefined, 16#2e}; -dec_huffman_lookup(16#16, 16#a) -> {more, undefined, 16#32}; -dec_huffman_lookup(16#16, 16#b) -> {more, undefined, 16#35}; -dec_huffman_lookup(16#16, 16#c) -> {more, undefined, 16#3a}; -dec_huffman_lookup(16#16, 16#d) -> {more, undefined, 16#3d}; -dec_huffman_lookup(16#16, 16#e) -> {more, undefined, 16#41}; -dec_huffman_lookup(16#16, 16#f) -> {ok, undefined, 16#44}; -dec_huffman_lookup(16#17, 16#0) -> {ok, 16#3d, 16#00}; -dec_huffman_lookup(16#17, 16#1) -> {ok, 16#41, 16#00}; -dec_huffman_lookup(16#17, 16#2) -> {ok, 16#5f, 16#00}; -dec_huffman_lookup(16#17, 16#3) -> {ok, 16#62, 16#00}; -dec_huffman_lookup(16#17, 16#4) -> {ok, 16#64, 16#00}; -dec_huffman_lookup(16#17, 16#5) -> {ok, 16#66, 16#00}; -dec_huffman_lookup(16#17, 16#6) -> {ok, 16#67, 16#00}; -dec_huffman_lookup(16#17, 16#7) -> {ok, 16#68, 16#00}; -dec_huffman_lookup(16#17, 16#8) -> {ok, 16#6c, 16#00}; -dec_huffman_lookup(16#17, 16#9) -> {ok, 16#6d, 16#00}; -dec_huffman_lookup(16#17, 16#a) -> {ok, 16#6e, 16#00}; -dec_huffman_lookup(16#17, 16#b) -> {ok, 16#70, 16#00}; -dec_huffman_lookup(16#17, 16#c) -> {ok, 16#72, 16#00}; -dec_huffman_lookup(16#17, 16#d) -> {ok, 16#75, 16#00}; -dec_huffman_lookup(16#17, 16#e) -> {more, undefined, 16#26}; -dec_huffman_lookup(16#17, 16#f) -> {more, undefined, 16#27}; -dec_huffman_lookup(16#18, 16#0) -> {more, 16#3d, 16#01}; -dec_huffman_lookup(16#18, 16#1) -> {ok, 16#3d, 16#16}; -dec_huffman_lookup(16#18, 16#2) -> {more, 16#41, 16#01}; -dec_huffman_lookup(16#18, 16#3) -> {ok, 16#41, 16#16}; -dec_huffman_lookup(16#18, 16#4) -> {more, 16#5f, 16#01}; -dec_huffman_lookup(16#18, 16#5) -> {ok, 16#5f, 16#16}; -dec_huffman_lookup(16#18, 16#6) -> {more, 16#62, 16#01}; -dec_huffman_lookup(16#18, 16#7) -> {ok, 16#62, 16#16}; -dec_huffman_lookup(16#18, 16#8) -> {more, 16#64, 16#01}; -dec_huffman_lookup(16#18, 16#9) -> {ok, 16#64, 16#16}; -dec_huffman_lookup(16#18, 16#a) -> {more, 16#66, 16#01}; -dec_huffman_lookup(16#18, 16#b) -> {ok, 16#66, 16#16}; -dec_huffman_lookup(16#18, 16#c) -> {more, 16#67, 16#01}; -dec_huffman_lookup(16#18, 16#d) -> {ok, 16#67, 16#16}; -dec_huffman_lookup(16#18, 16#e) -> {more, 16#68, 16#01}; -dec_huffman_lookup(16#18, 16#f) -> {ok, 16#68, 16#16}; -dec_huffman_lookup(16#19, 16#0) -> {more, 16#3d, 16#02}; -dec_huffman_lookup(16#19, 16#1) -> {more, 16#3d, 16#09}; -dec_huffman_lookup(16#19, 16#2) -> {more, 16#3d, 16#17}; -dec_huffman_lookup(16#19, 16#3) -> {ok, 16#3d, 16#28}; -dec_huffman_lookup(16#19, 16#4) -> {more, 16#41, 16#02}; -dec_huffman_lookup(16#19, 16#5) -> {more, 16#41, 16#09}; -dec_huffman_lookup(16#19, 16#6) -> {more, 16#41, 16#17}; -dec_huffman_lookup(16#19, 16#7) -> {ok, 16#41, 16#28}; -dec_huffman_lookup(16#19, 16#8) -> {more, 16#5f, 16#02}; -dec_huffman_lookup(16#19, 16#9) -> {more, 16#5f, 16#09}; -dec_huffman_lookup(16#19, 16#a) -> {more, 16#5f, 16#17}; -dec_huffman_lookup(16#19, 16#b) -> {ok, 16#5f, 16#28}; -dec_huffman_lookup(16#19, 16#c) -> {more, 16#62, 16#02}; -dec_huffman_lookup(16#19, 16#d) -> {more, 16#62, 16#09}; -dec_huffman_lookup(16#19, 16#e) -> {more, 16#62, 16#17}; -dec_huffman_lookup(16#19, 16#f) -> {ok, 16#62, 16#28}; -dec_huffman_lookup(16#1a, 16#0) -> {more, 16#3d, 16#03}; -dec_huffman_lookup(16#1a, 16#1) -> {more, 16#3d, 16#06}; -dec_huffman_lookup(16#1a, 16#2) -> {more, 16#3d, 16#0a}; -dec_huffman_lookup(16#1a, 16#3) -> {more, 16#3d, 16#0f}; -dec_huffman_lookup(16#1a, 16#4) -> {more, 16#3d, 16#18}; -dec_huffman_lookup(16#1a, 16#5) -> {more, 16#3d, 16#1f}; -dec_huffman_lookup(16#1a, 16#6) -> {more, 16#3d, 16#29}; -dec_huffman_lookup(16#1a, 16#7) -> {ok, 16#3d, 16#38}; -dec_huffman_lookup(16#1a, 16#8) -> {more, 16#41, 16#03}; -dec_huffman_lookup(16#1a, 16#9) -> {more, 16#41, 16#06}; -dec_huffman_lookup(16#1a, 16#a) -> {more, 16#41, 16#0a}; -dec_huffman_lookup(16#1a, 16#b) -> {more, 16#41, 16#0f}; -dec_huffman_lookup(16#1a, 16#c) -> {more, 16#41, 16#18}; -dec_huffman_lookup(16#1a, 16#d) -> {more, 16#41, 16#1f}; -dec_huffman_lookup(16#1a, 16#e) -> {more, 16#41, 16#29}; -dec_huffman_lookup(16#1a, 16#f) -> {ok, 16#41, 16#38}; -dec_huffman_lookup(16#1b, 16#0) -> {more, 16#5f, 16#03}; -dec_huffman_lookup(16#1b, 16#1) -> {more, 16#5f, 16#06}; -dec_huffman_lookup(16#1b, 16#2) -> {more, 16#5f, 16#0a}; -dec_huffman_lookup(16#1b, 16#3) -> {more, 16#5f, 16#0f}; -dec_huffman_lookup(16#1b, 16#4) -> {more, 16#5f, 16#18}; -dec_huffman_lookup(16#1b, 16#5) -> {more, 16#5f, 16#1f}; -dec_huffman_lookup(16#1b, 16#6) -> {more, 16#5f, 16#29}; -dec_huffman_lookup(16#1b, 16#7) -> {ok, 16#5f, 16#38}; -dec_huffman_lookup(16#1b, 16#8) -> {more, 16#62, 16#03}; -dec_huffman_lookup(16#1b, 16#9) -> {more, 16#62, 16#06}; -dec_huffman_lookup(16#1b, 16#a) -> {more, 16#62, 16#0a}; -dec_huffman_lookup(16#1b, 16#b) -> {more, 16#62, 16#0f}; -dec_huffman_lookup(16#1b, 16#c) -> {more, 16#62, 16#18}; -dec_huffman_lookup(16#1b, 16#d) -> {more, 16#62, 16#1f}; -dec_huffman_lookup(16#1b, 16#e) -> {more, 16#62, 16#29}; -dec_huffman_lookup(16#1b, 16#f) -> {ok, 16#62, 16#38}; -dec_huffman_lookup(16#1c, 16#0) -> {more, 16#64, 16#02}; -dec_huffman_lookup(16#1c, 16#1) -> {more, 16#64, 16#09}; -dec_huffman_lookup(16#1c, 16#2) -> {more, 16#64, 16#17}; -dec_huffman_lookup(16#1c, 16#3) -> {ok, 16#64, 16#28}; -dec_huffman_lookup(16#1c, 16#4) -> {more, 16#66, 16#02}; -dec_huffman_lookup(16#1c, 16#5) -> {more, 16#66, 16#09}; -dec_huffman_lookup(16#1c, 16#6) -> {more, 16#66, 16#17}; -dec_huffman_lookup(16#1c, 16#7) -> {ok, 16#66, 16#28}; -dec_huffman_lookup(16#1c, 16#8) -> {more, 16#67, 16#02}; -dec_huffman_lookup(16#1c, 16#9) -> {more, 16#67, 16#09}; -dec_huffman_lookup(16#1c, 16#a) -> {more, 16#67, 16#17}; -dec_huffman_lookup(16#1c, 16#b) -> {ok, 16#67, 16#28}; -dec_huffman_lookup(16#1c, 16#c) -> {more, 16#68, 16#02}; -dec_huffman_lookup(16#1c, 16#d) -> {more, 16#68, 16#09}; -dec_huffman_lookup(16#1c, 16#e) -> {more, 16#68, 16#17}; -dec_huffman_lookup(16#1c, 16#f) -> {ok, 16#68, 16#28}; -dec_huffman_lookup(16#1d, 16#0) -> {more, 16#64, 16#03}; -dec_huffman_lookup(16#1d, 16#1) -> {more, 16#64, 16#06}; -dec_huffman_lookup(16#1d, 16#2) -> {more, 16#64, 16#0a}; -dec_huffman_lookup(16#1d, 16#3) -> {more, 16#64, 16#0f}; -dec_huffman_lookup(16#1d, 16#4) -> {more, 16#64, 16#18}; -dec_huffman_lookup(16#1d, 16#5) -> {more, 16#64, 16#1f}; -dec_huffman_lookup(16#1d, 16#6) -> {more, 16#64, 16#29}; -dec_huffman_lookup(16#1d, 16#7) -> {ok, 16#64, 16#38}; -dec_huffman_lookup(16#1d, 16#8) -> {more, 16#66, 16#03}; -dec_huffman_lookup(16#1d, 16#9) -> {more, 16#66, 16#06}; -dec_huffman_lookup(16#1d, 16#a) -> {more, 16#66, 16#0a}; -dec_huffman_lookup(16#1d, 16#b) -> {more, 16#66, 16#0f}; -dec_huffman_lookup(16#1d, 16#c) -> {more, 16#66, 16#18}; -dec_huffman_lookup(16#1d, 16#d) -> {more, 16#66, 16#1f}; -dec_huffman_lookup(16#1d, 16#e) -> {more, 16#66, 16#29}; -dec_huffman_lookup(16#1d, 16#f) -> {ok, 16#66, 16#38}; -dec_huffman_lookup(16#1e, 16#0) -> {more, 16#67, 16#03}; -dec_huffman_lookup(16#1e, 16#1) -> {more, 16#67, 16#06}; -dec_huffman_lookup(16#1e, 16#2) -> {more, 16#67, 16#0a}; -dec_huffman_lookup(16#1e, 16#3) -> {more, 16#67, 16#0f}; -dec_huffman_lookup(16#1e, 16#4) -> {more, 16#67, 16#18}; -dec_huffman_lookup(16#1e, 16#5) -> {more, 16#67, 16#1f}; -dec_huffman_lookup(16#1e, 16#6) -> {more, 16#67, 16#29}; -dec_huffman_lookup(16#1e, 16#7) -> {ok, 16#67, 16#38}; -dec_huffman_lookup(16#1e, 16#8) -> {more, 16#68, 16#03}; -dec_huffman_lookup(16#1e, 16#9) -> {more, 16#68, 16#06}; -dec_huffman_lookup(16#1e, 16#a) -> {more, 16#68, 16#0a}; -dec_huffman_lookup(16#1e, 16#b) -> {more, 16#68, 16#0f}; -dec_huffman_lookup(16#1e, 16#c) -> {more, 16#68, 16#18}; -dec_huffman_lookup(16#1e, 16#d) -> {more, 16#68, 16#1f}; -dec_huffman_lookup(16#1e, 16#e) -> {more, 16#68, 16#29}; -dec_huffman_lookup(16#1e, 16#f) -> {ok, 16#68, 16#38}; -dec_huffman_lookup(16#1f, 16#0) -> {more, 16#6c, 16#01}; -dec_huffman_lookup(16#1f, 16#1) -> {ok, 16#6c, 16#16}; -dec_huffman_lookup(16#1f, 16#2) -> {more, 16#6d, 16#01}; -dec_huffman_lookup(16#1f, 16#3) -> {ok, 16#6d, 16#16}; -dec_huffman_lookup(16#1f, 16#4) -> {more, 16#6e, 16#01}; -dec_huffman_lookup(16#1f, 16#5) -> {ok, 16#6e, 16#16}; -dec_huffman_lookup(16#1f, 16#6) -> {more, 16#70, 16#01}; -dec_huffman_lookup(16#1f, 16#7) -> {ok, 16#70, 16#16}; -dec_huffman_lookup(16#1f, 16#8) -> {more, 16#72, 16#01}; -dec_huffman_lookup(16#1f, 16#9) -> {ok, 16#72, 16#16}; -dec_huffman_lookup(16#1f, 16#a) -> {more, 16#75, 16#01}; -dec_huffman_lookup(16#1f, 16#b) -> {ok, 16#75, 16#16}; -dec_huffman_lookup(16#1f, 16#c) -> {ok, 16#3a, 16#00}; -dec_huffman_lookup(16#1f, 16#d) -> {ok, 16#42, 16#00}; -dec_huffman_lookup(16#1f, 16#e) -> {ok, 16#43, 16#00}; -dec_huffman_lookup(16#1f, 16#f) -> {ok, 16#44, 16#00}; -dec_huffman_lookup(16#20, 16#0) -> {more, 16#6c, 16#02}; -dec_huffman_lookup(16#20, 16#1) -> {more, 16#6c, 16#09}; -dec_huffman_lookup(16#20, 16#2) -> {more, 16#6c, 16#17}; -dec_huffman_lookup(16#20, 16#3) -> {ok, 16#6c, 16#28}; -dec_huffman_lookup(16#20, 16#4) -> {more, 16#6d, 16#02}; -dec_huffman_lookup(16#20, 16#5) -> {more, 16#6d, 16#09}; -dec_huffman_lookup(16#20, 16#6) -> {more, 16#6d, 16#17}; -dec_huffman_lookup(16#20, 16#7) -> {ok, 16#6d, 16#28}; -dec_huffman_lookup(16#20, 16#8) -> {more, 16#6e, 16#02}; -dec_huffman_lookup(16#20, 16#9) -> {more, 16#6e, 16#09}; -dec_huffman_lookup(16#20, 16#a) -> {more, 16#6e, 16#17}; -dec_huffman_lookup(16#20, 16#b) -> {ok, 16#6e, 16#28}; -dec_huffman_lookup(16#20, 16#c) -> {more, 16#70, 16#02}; -dec_huffman_lookup(16#20, 16#d) -> {more, 16#70, 16#09}; -dec_huffman_lookup(16#20, 16#e) -> {more, 16#70, 16#17}; -dec_huffman_lookup(16#20, 16#f) -> {ok, 16#70, 16#28}; -dec_huffman_lookup(16#21, 16#0) -> {more, 16#6c, 16#03}; -dec_huffman_lookup(16#21, 16#1) -> {more, 16#6c, 16#06}; -dec_huffman_lookup(16#21, 16#2) -> {more, 16#6c, 16#0a}; -dec_huffman_lookup(16#21, 16#3) -> {more, 16#6c, 16#0f}; -dec_huffman_lookup(16#21, 16#4) -> {more, 16#6c, 16#18}; -dec_huffman_lookup(16#21, 16#5) -> {more, 16#6c, 16#1f}; -dec_huffman_lookup(16#21, 16#6) -> {more, 16#6c, 16#29}; -dec_huffman_lookup(16#21, 16#7) -> {ok, 16#6c, 16#38}; -dec_huffman_lookup(16#21, 16#8) -> {more, 16#6d, 16#03}; -dec_huffman_lookup(16#21, 16#9) -> {more, 16#6d, 16#06}; -dec_huffman_lookup(16#21, 16#a) -> {more, 16#6d, 16#0a}; -dec_huffman_lookup(16#21, 16#b) -> {more, 16#6d, 16#0f}; -dec_huffman_lookup(16#21, 16#c) -> {more, 16#6d, 16#18}; -dec_huffman_lookup(16#21, 16#d) -> {more, 16#6d, 16#1f}; -dec_huffman_lookup(16#21, 16#e) -> {more, 16#6d, 16#29}; -dec_huffman_lookup(16#21, 16#f) -> {ok, 16#6d, 16#38}; -dec_huffman_lookup(16#22, 16#0) -> {more, 16#6e, 16#03}; -dec_huffman_lookup(16#22, 16#1) -> {more, 16#6e, 16#06}; -dec_huffman_lookup(16#22, 16#2) -> {more, 16#6e, 16#0a}; -dec_huffman_lookup(16#22, 16#3) -> {more, 16#6e, 16#0f}; -dec_huffman_lookup(16#22, 16#4) -> {more, 16#6e, 16#18}; -dec_huffman_lookup(16#22, 16#5) -> {more, 16#6e, 16#1f}; -dec_huffman_lookup(16#22, 16#6) -> {more, 16#6e, 16#29}; -dec_huffman_lookup(16#22, 16#7) -> {ok, 16#6e, 16#38}; -dec_huffman_lookup(16#22, 16#8) -> {more, 16#70, 16#03}; -dec_huffman_lookup(16#22, 16#9) -> {more, 16#70, 16#06}; -dec_huffman_lookup(16#22, 16#a) -> {more, 16#70, 16#0a}; -dec_huffman_lookup(16#22, 16#b) -> {more, 16#70, 16#0f}; -dec_huffman_lookup(16#22, 16#c) -> {more, 16#70, 16#18}; -dec_huffman_lookup(16#22, 16#d) -> {more, 16#70, 16#1f}; -dec_huffman_lookup(16#22, 16#e) -> {more, 16#70, 16#29}; -dec_huffman_lookup(16#22, 16#f) -> {ok, 16#70, 16#38}; -dec_huffman_lookup(16#23, 16#0) -> {more, 16#72, 16#02}; -dec_huffman_lookup(16#23, 16#1) -> {more, 16#72, 16#09}; -dec_huffman_lookup(16#23, 16#2) -> {more, 16#72, 16#17}; -dec_huffman_lookup(16#23, 16#3) -> {ok, 16#72, 16#28}; -dec_huffman_lookup(16#23, 16#4) -> {more, 16#75, 16#02}; -dec_huffman_lookup(16#23, 16#5) -> {more, 16#75, 16#09}; -dec_huffman_lookup(16#23, 16#6) -> {more, 16#75, 16#17}; -dec_huffman_lookup(16#23, 16#7) -> {ok, 16#75, 16#28}; -dec_huffman_lookup(16#23, 16#8) -> {more, 16#3a, 16#01}; -dec_huffman_lookup(16#23, 16#9) -> {ok, 16#3a, 16#16}; -dec_huffman_lookup(16#23, 16#a) -> {more, 16#42, 16#01}; -dec_huffman_lookup(16#23, 16#b) -> {ok, 16#42, 16#16}; -dec_huffman_lookup(16#23, 16#c) -> {more, 16#43, 16#01}; -dec_huffman_lookup(16#23, 16#d) -> {ok, 16#43, 16#16}; -dec_huffman_lookup(16#23, 16#e) -> {more, 16#44, 16#01}; -dec_huffman_lookup(16#23, 16#f) -> {ok, 16#44, 16#16}; -dec_huffman_lookup(16#24, 16#0) -> {more, 16#72, 16#03}; -dec_huffman_lookup(16#24, 16#1) -> {more, 16#72, 16#06}; -dec_huffman_lookup(16#24, 16#2) -> {more, 16#72, 16#0a}; -dec_huffman_lookup(16#24, 16#3) -> {more, 16#72, 16#0f}; -dec_huffman_lookup(16#24, 16#4) -> {more, 16#72, 16#18}; -dec_huffman_lookup(16#24, 16#5) -> {more, 16#72, 16#1f}; -dec_huffman_lookup(16#24, 16#6) -> {more, 16#72, 16#29}; -dec_huffman_lookup(16#24, 16#7) -> {ok, 16#72, 16#38}; -dec_huffman_lookup(16#24, 16#8) -> {more, 16#75, 16#03}; -dec_huffman_lookup(16#24, 16#9) -> {more, 16#75, 16#06}; -dec_huffman_lookup(16#24, 16#a) -> {more, 16#75, 16#0a}; -dec_huffman_lookup(16#24, 16#b) -> {more, 16#75, 16#0f}; -dec_huffman_lookup(16#24, 16#c) -> {more, 16#75, 16#18}; -dec_huffman_lookup(16#24, 16#d) -> {more, 16#75, 16#1f}; -dec_huffman_lookup(16#24, 16#e) -> {more, 16#75, 16#29}; -dec_huffman_lookup(16#24, 16#f) -> {ok, 16#75, 16#38}; -dec_huffman_lookup(16#25, 16#0) -> {more, 16#3a, 16#02}; -dec_huffman_lookup(16#25, 16#1) -> {more, 16#3a, 16#09}; -dec_huffman_lookup(16#25, 16#2) -> {more, 16#3a, 16#17}; -dec_huffman_lookup(16#25, 16#3) -> {ok, 16#3a, 16#28}; -dec_huffman_lookup(16#25, 16#4) -> {more, 16#42, 16#02}; -dec_huffman_lookup(16#25, 16#5) -> {more, 16#42, 16#09}; -dec_huffman_lookup(16#25, 16#6) -> {more, 16#42, 16#17}; -dec_huffman_lookup(16#25, 16#7) -> {ok, 16#42, 16#28}; -dec_huffman_lookup(16#25, 16#8) -> {more, 16#43, 16#02}; -dec_huffman_lookup(16#25, 16#9) -> {more, 16#43, 16#09}; -dec_huffman_lookup(16#25, 16#a) -> {more, 16#43, 16#17}; -dec_huffman_lookup(16#25, 16#b) -> {ok, 16#43, 16#28}; -dec_huffman_lookup(16#25, 16#c) -> {more, 16#44, 16#02}; -dec_huffman_lookup(16#25, 16#d) -> {more, 16#44, 16#09}; -dec_huffman_lookup(16#25, 16#e) -> {more, 16#44, 16#17}; -dec_huffman_lookup(16#25, 16#f) -> {ok, 16#44, 16#28}; -dec_huffman_lookup(16#26, 16#0) -> {more, 16#3a, 16#03}; -dec_huffman_lookup(16#26, 16#1) -> {more, 16#3a, 16#06}; -dec_huffman_lookup(16#26, 16#2) -> {more, 16#3a, 16#0a}; -dec_huffman_lookup(16#26, 16#3) -> {more, 16#3a, 16#0f}; -dec_huffman_lookup(16#26, 16#4) -> {more, 16#3a, 16#18}; -dec_huffman_lookup(16#26, 16#5) -> {more, 16#3a, 16#1f}; -dec_huffman_lookup(16#26, 16#6) -> {more, 16#3a, 16#29}; -dec_huffman_lookup(16#26, 16#7) -> {ok, 16#3a, 16#38}; -dec_huffman_lookup(16#26, 16#8) -> {more, 16#42, 16#03}; -dec_huffman_lookup(16#26, 16#9) -> {more, 16#42, 16#06}; -dec_huffman_lookup(16#26, 16#a) -> {more, 16#42, 16#0a}; -dec_huffman_lookup(16#26, 16#b) -> {more, 16#42, 16#0f}; -dec_huffman_lookup(16#26, 16#c) -> {more, 16#42, 16#18}; -dec_huffman_lookup(16#26, 16#d) -> {more, 16#42, 16#1f}; -dec_huffman_lookup(16#26, 16#e) -> {more, 16#42, 16#29}; -dec_huffman_lookup(16#26, 16#f) -> {ok, 16#42, 16#38}; -dec_huffman_lookup(16#27, 16#0) -> {more, 16#43, 16#03}; -dec_huffman_lookup(16#27, 16#1) -> {more, 16#43, 16#06}; -dec_huffman_lookup(16#27, 16#2) -> {more, 16#43, 16#0a}; -dec_huffman_lookup(16#27, 16#3) -> {more, 16#43, 16#0f}; -dec_huffman_lookup(16#27, 16#4) -> {more, 16#43, 16#18}; -dec_huffman_lookup(16#27, 16#5) -> {more, 16#43, 16#1f}; -dec_huffman_lookup(16#27, 16#6) -> {more, 16#43, 16#29}; -dec_huffman_lookup(16#27, 16#7) -> {ok, 16#43, 16#38}; -dec_huffman_lookup(16#27, 16#8) -> {more, 16#44, 16#03}; -dec_huffman_lookup(16#27, 16#9) -> {more, 16#44, 16#06}; -dec_huffman_lookup(16#27, 16#a) -> {more, 16#44, 16#0a}; -dec_huffman_lookup(16#27, 16#b) -> {more, 16#44, 16#0f}; -dec_huffman_lookup(16#27, 16#c) -> {more, 16#44, 16#18}; -dec_huffman_lookup(16#27, 16#d) -> {more, 16#44, 16#1f}; -dec_huffman_lookup(16#27, 16#e) -> {more, 16#44, 16#29}; -dec_huffman_lookup(16#27, 16#f) -> {ok, 16#44, 16#38}; -dec_huffman_lookup(16#28, 16#0) -> {more, undefined, 16#2c}; -dec_huffman_lookup(16#28, 16#1) -> {more, undefined, 16#2d}; -dec_huffman_lookup(16#28, 16#2) -> {more, undefined, 16#2f}; -dec_huffman_lookup(16#28, 16#3) -> {more, undefined, 16#30}; -dec_huffman_lookup(16#28, 16#4) -> {more, undefined, 16#33}; -dec_huffman_lookup(16#28, 16#5) -> {more, undefined, 16#34}; -dec_huffman_lookup(16#28, 16#6) -> {more, undefined, 16#36}; -dec_huffman_lookup(16#28, 16#7) -> {more, undefined, 16#37}; -dec_huffman_lookup(16#28, 16#8) -> {more, undefined, 16#3b}; -dec_huffman_lookup(16#28, 16#9) -> {more, undefined, 16#3c}; -dec_huffman_lookup(16#28, 16#a) -> {more, undefined, 16#3e}; -dec_huffman_lookup(16#28, 16#b) -> {more, undefined, 16#3f}; -dec_huffman_lookup(16#28, 16#c) -> {more, undefined, 16#42}; -dec_huffman_lookup(16#28, 16#d) -> {more, undefined, 16#43}; -dec_huffman_lookup(16#28, 16#e) -> {more, undefined, 16#45}; -dec_huffman_lookup(16#28, 16#f) -> {ok, undefined, 16#48}; -dec_huffman_lookup(16#29, 16#0) -> {ok, 16#45, 16#00}; -dec_huffman_lookup(16#29, 16#1) -> {ok, 16#46, 16#00}; -dec_huffman_lookup(16#29, 16#2) -> {ok, 16#47, 16#00}; -dec_huffman_lookup(16#29, 16#3) -> {ok, 16#48, 16#00}; -dec_huffman_lookup(16#29, 16#4) -> {ok, 16#49, 16#00}; -dec_huffman_lookup(16#29, 16#5) -> {ok, 16#4a, 16#00}; -dec_huffman_lookup(16#29, 16#6) -> {ok, 16#4b, 16#00}; -dec_huffman_lookup(16#29, 16#7) -> {ok, 16#4c, 16#00}; -dec_huffman_lookup(16#29, 16#8) -> {ok, 16#4d, 16#00}; -dec_huffman_lookup(16#29, 16#9) -> {ok, 16#4e, 16#00}; -dec_huffman_lookup(16#29, 16#a) -> {ok, 16#4f, 16#00}; -dec_huffman_lookup(16#29, 16#b) -> {ok, 16#50, 16#00}; -dec_huffman_lookup(16#29, 16#c) -> {ok, 16#51, 16#00}; -dec_huffman_lookup(16#29, 16#d) -> {ok, 16#52, 16#00}; -dec_huffman_lookup(16#29, 16#e) -> {ok, 16#53, 16#00}; -dec_huffman_lookup(16#29, 16#f) -> {ok, 16#54, 16#00}; -dec_huffman_lookup(16#2a, 16#0) -> {more, 16#45, 16#01}; -dec_huffman_lookup(16#2a, 16#1) -> {ok, 16#45, 16#16}; -dec_huffman_lookup(16#2a, 16#2) -> {more, 16#46, 16#01}; -dec_huffman_lookup(16#2a, 16#3) -> {ok, 16#46, 16#16}; -dec_huffman_lookup(16#2a, 16#4) -> {more, 16#47, 16#01}; -dec_huffman_lookup(16#2a, 16#5) -> {ok, 16#47, 16#16}; -dec_huffman_lookup(16#2a, 16#6) -> {more, 16#48, 16#01}; -dec_huffman_lookup(16#2a, 16#7) -> {ok, 16#48, 16#16}; -dec_huffman_lookup(16#2a, 16#8) -> {more, 16#49, 16#01}; -dec_huffman_lookup(16#2a, 16#9) -> {ok, 16#49, 16#16}; -dec_huffman_lookup(16#2a, 16#a) -> {more, 16#4a, 16#01}; -dec_huffman_lookup(16#2a, 16#b) -> {ok, 16#4a, 16#16}; -dec_huffman_lookup(16#2a, 16#c) -> {more, 16#4b, 16#01}; -dec_huffman_lookup(16#2a, 16#d) -> {ok, 16#4b, 16#16}; -dec_huffman_lookup(16#2a, 16#e) -> {more, 16#4c, 16#01}; -dec_huffman_lookup(16#2a, 16#f) -> {ok, 16#4c, 16#16}; -dec_huffman_lookup(16#2b, 16#0) -> {more, 16#45, 16#02}; -dec_huffman_lookup(16#2b, 16#1) -> {more, 16#45, 16#09}; -dec_huffman_lookup(16#2b, 16#2) -> {more, 16#45, 16#17}; -dec_huffman_lookup(16#2b, 16#3) -> {ok, 16#45, 16#28}; -dec_huffman_lookup(16#2b, 16#4) -> {more, 16#46, 16#02}; -dec_huffman_lookup(16#2b, 16#5) -> {more, 16#46, 16#09}; -dec_huffman_lookup(16#2b, 16#6) -> {more, 16#46, 16#17}; -dec_huffman_lookup(16#2b, 16#7) -> {ok, 16#46, 16#28}; -dec_huffman_lookup(16#2b, 16#8) -> {more, 16#47, 16#02}; -dec_huffman_lookup(16#2b, 16#9) -> {more, 16#47, 16#09}; -dec_huffman_lookup(16#2b, 16#a) -> {more, 16#47, 16#17}; -dec_huffman_lookup(16#2b, 16#b) -> {ok, 16#47, 16#28}; -dec_huffman_lookup(16#2b, 16#c) -> {more, 16#48, 16#02}; -dec_huffman_lookup(16#2b, 16#d) -> {more, 16#48, 16#09}; -dec_huffman_lookup(16#2b, 16#e) -> {more, 16#48, 16#17}; -dec_huffman_lookup(16#2b, 16#f) -> {ok, 16#48, 16#28}; -dec_huffman_lookup(16#2c, 16#0) -> {more, 16#45, 16#03}; -dec_huffman_lookup(16#2c, 16#1) -> {more, 16#45, 16#06}; -dec_huffman_lookup(16#2c, 16#2) -> {more, 16#45, 16#0a}; -dec_huffman_lookup(16#2c, 16#3) -> {more, 16#45, 16#0f}; -dec_huffman_lookup(16#2c, 16#4) -> {more, 16#45, 16#18}; -dec_huffman_lookup(16#2c, 16#5) -> {more, 16#45, 16#1f}; -dec_huffman_lookup(16#2c, 16#6) -> {more, 16#45, 16#29}; -dec_huffman_lookup(16#2c, 16#7) -> {ok, 16#45, 16#38}; -dec_huffman_lookup(16#2c, 16#8) -> {more, 16#46, 16#03}; -dec_huffman_lookup(16#2c, 16#9) -> {more, 16#46, 16#06}; -dec_huffman_lookup(16#2c, 16#a) -> {more, 16#46, 16#0a}; -dec_huffman_lookup(16#2c, 16#b) -> {more, 16#46, 16#0f}; -dec_huffman_lookup(16#2c, 16#c) -> {more, 16#46, 16#18}; -dec_huffman_lookup(16#2c, 16#d) -> {more, 16#46, 16#1f}; -dec_huffman_lookup(16#2c, 16#e) -> {more, 16#46, 16#29}; -dec_huffman_lookup(16#2c, 16#f) -> {ok, 16#46, 16#38}; -dec_huffman_lookup(16#2d, 16#0) -> {more, 16#47, 16#03}; -dec_huffman_lookup(16#2d, 16#1) -> {more, 16#47, 16#06}; -dec_huffman_lookup(16#2d, 16#2) -> {more, 16#47, 16#0a}; -dec_huffman_lookup(16#2d, 16#3) -> {more, 16#47, 16#0f}; -dec_huffman_lookup(16#2d, 16#4) -> {more, 16#47, 16#18}; -dec_huffman_lookup(16#2d, 16#5) -> {more, 16#47, 16#1f}; -dec_huffman_lookup(16#2d, 16#6) -> {more, 16#47, 16#29}; -dec_huffman_lookup(16#2d, 16#7) -> {ok, 16#47, 16#38}; -dec_huffman_lookup(16#2d, 16#8) -> {more, 16#48, 16#03}; -dec_huffman_lookup(16#2d, 16#9) -> {more, 16#48, 16#06}; -dec_huffman_lookup(16#2d, 16#a) -> {more, 16#48, 16#0a}; -dec_huffman_lookup(16#2d, 16#b) -> {more, 16#48, 16#0f}; -dec_huffman_lookup(16#2d, 16#c) -> {more, 16#48, 16#18}; -dec_huffman_lookup(16#2d, 16#d) -> {more, 16#48, 16#1f}; -dec_huffman_lookup(16#2d, 16#e) -> {more, 16#48, 16#29}; -dec_huffman_lookup(16#2d, 16#f) -> {ok, 16#48, 16#38}; -dec_huffman_lookup(16#2e, 16#0) -> {more, 16#49, 16#02}; -dec_huffman_lookup(16#2e, 16#1) -> {more, 16#49, 16#09}; -dec_huffman_lookup(16#2e, 16#2) -> {more, 16#49, 16#17}; -dec_huffman_lookup(16#2e, 16#3) -> {ok, 16#49, 16#28}; -dec_huffman_lookup(16#2e, 16#4) -> {more, 16#4a, 16#02}; -dec_huffman_lookup(16#2e, 16#5) -> {more, 16#4a, 16#09}; -dec_huffman_lookup(16#2e, 16#6) -> {more, 16#4a, 16#17}; -dec_huffman_lookup(16#2e, 16#7) -> {ok, 16#4a, 16#28}; -dec_huffman_lookup(16#2e, 16#8) -> {more, 16#4b, 16#02}; -dec_huffman_lookup(16#2e, 16#9) -> {more, 16#4b, 16#09}; -dec_huffman_lookup(16#2e, 16#a) -> {more, 16#4b, 16#17}; -dec_huffman_lookup(16#2e, 16#b) -> {ok, 16#4b, 16#28}; -dec_huffman_lookup(16#2e, 16#c) -> {more, 16#4c, 16#02}; -dec_huffman_lookup(16#2e, 16#d) -> {more, 16#4c, 16#09}; -dec_huffman_lookup(16#2e, 16#e) -> {more, 16#4c, 16#17}; -dec_huffman_lookup(16#2e, 16#f) -> {ok, 16#4c, 16#28}; -dec_huffman_lookup(16#2f, 16#0) -> {more, 16#49, 16#03}; -dec_huffman_lookup(16#2f, 16#1) -> {more, 16#49, 16#06}; -dec_huffman_lookup(16#2f, 16#2) -> {more, 16#49, 16#0a}; -dec_huffman_lookup(16#2f, 16#3) -> {more, 16#49, 16#0f}; -dec_huffman_lookup(16#2f, 16#4) -> {more, 16#49, 16#18}; -dec_huffman_lookup(16#2f, 16#5) -> {more, 16#49, 16#1f}; -dec_huffman_lookup(16#2f, 16#6) -> {more, 16#49, 16#29}; -dec_huffman_lookup(16#2f, 16#7) -> {ok, 16#49, 16#38}; -dec_huffman_lookup(16#2f, 16#8) -> {more, 16#4a, 16#03}; -dec_huffman_lookup(16#2f, 16#9) -> {more, 16#4a, 16#06}; -dec_huffman_lookup(16#2f, 16#a) -> {more, 16#4a, 16#0a}; -dec_huffman_lookup(16#2f, 16#b) -> {more, 16#4a, 16#0f}; -dec_huffman_lookup(16#2f, 16#c) -> {more, 16#4a, 16#18}; -dec_huffman_lookup(16#2f, 16#d) -> {more, 16#4a, 16#1f}; -dec_huffman_lookup(16#2f, 16#e) -> {more, 16#4a, 16#29}; -dec_huffman_lookup(16#2f, 16#f) -> {ok, 16#4a, 16#38}; -dec_huffman_lookup(16#30, 16#0) -> {more, 16#4b, 16#03}; -dec_huffman_lookup(16#30, 16#1) -> {more, 16#4b, 16#06}; -dec_huffman_lookup(16#30, 16#2) -> {more, 16#4b, 16#0a}; -dec_huffman_lookup(16#30, 16#3) -> {more, 16#4b, 16#0f}; -dec_huffman_lookup(16#30, 16#4) -> {more, 16#4b, 16#18}; -dec_huffman_lookup(16#30, 16#5) -> {more, 16#4b, 16#1f}; -dec_huffman_lookup(16#30, 16#6) -> {more, 16#4b, 16#29}; -dec_huffman_lookup(16#30, 16#7) -> {ok, 16#4b, 16#38}; -dec_huffman_lookup(16#30, 16#8) -> {more, 16#4c, 16#03}; -dec_huffman_lookup(16#30, 16#9) -> {more, 16#4c, 16#06}; -dec_huffman_lookup(16#30, 16#a) -> {more, 16#4c, 16#0a}; -dec_huffman_lookup(16#30, 16#b) -> {more, 16#4c, 16#0f}; -dec_huffman_lookup(16#30, 16#c) -> {more, 16#4c, 16#18}; -dec_huffman_lookup(16#30, 16#d) -> {more, 16#4c, 16#1f}; -dec_huffman_lookup(16#30, 16#e) -> {more, 16#4c, 16#29}; -dec_huffman_lookup(16#30, 16#f) -> {ok, 16#4c, 16#38}; -dec_huffman_lookup(16#31, 16#0) -> {more, 16#4d, 16#01}; -dec_huffman_lookup(16#31, 16#1) -> {ok, 16#4d, 16#16}; -dec_huffman_lookup(16#31, 16#2) -> {more, 16#4e, 16#01}; -dec_huffman_lookup(16#31, 16#3) -> {ok, 16#4e, 16#16}; -dec_huffman_lookup(16#31, 16#4) -> {more, 16#4f, 16#01}; -dec_huffman_lookup(16#31, 16#5) -> {ok, 16#4f, 16#16}; -dec_huffman_lookup(16#31, 16#6) -> {more, 16#50, 16#01}; -dec_huffman_lookup(16#31, 16#7) -> {ok, 16#50, 16#16}; -dec_huffman_lookup(16#31, 16#8) -> {more, 16#51, 16#01}; -dec_huffman_lookup(16#31, 16#9) -> {ok, 16#51, 16#16}; -dec_huffman_lookup(16#31, 16#a) -> {more, 16#52, 16#01}; -dec_huffman_lookup(16#31, 16#b) -> {ok, 16#52, 16#16}; -dec_huffman_lookup(16#31, 16#c) -> {more, 16#53, 16#01}; -dec_huffman_lookup(16#31, 16#d) -> {ok, 16#53, 16#16}; -dec_huffman_lookup(16#31, 16#e) -> {more, 16#54, 16#01}; -dec_huffman_lookup(16#31, 16#f) -> {ok, 16#54, 16#16}; -dec_huffman_lookup(16#32, 16#0) -> {more, 16#4d, 16#02}; -dec_huffman_lookup(16#32, 16#1) -> {more, 16#4d, 16#09}; -dec_huffman_lookup(16#32, 16#2) -> {more, 16#4d, 16#17}; -dec_huffman_lookup(16#32, 16#3) -> {ok, 16#4d, 16#28}; -dec_huffman_lookup(16#32, 16#4) -> {more, 16#4e, 16#02}; -dec_huffman_lookup(16#32, 16#5) -> {more, 16#4e, 16#09}; -dec_huffman_lookup(16#32, 16#6) -> {more, 16#4e, 16#17}; -dec_huffman_lookup(16#32, 16#7) -> {ok, 16#4e, 16#28}; -dec_huffman_lookup(16#32, 16#8) -> {more, 16#4f, 16#02}; -dec_huffman_lookup(16#32, 16#9) -> {more, 16#4f, 16#09}; -dec_huffman_lookup(16#32, 16#a) -> {more, 16#4f, 16#17}; -dec_huffman_lookup(16#32, 16#b) -> {ok, 16#4f, 16#28}; -dec_huffman_lookup(16#32, 16#c) -> {more, 16#50, 16#02}; -dec_huffman_lookup(16#32, 16#d) -> {more, 16#50, 16#09}; -dec_huffman_lookup(16#32, 16#e) -> {more, 16#50, 16#17}; -dec_huffman_lookup(16#32, 16#f) -> {ok, 16#50, 16#28}; -dec_huffman_lookup(16#33, 16#0) -> {more, 16#4d, 16#03}; -dec_huffman_lookup(16#33, 16#1) -> {more, 16#4d, 16#06}; -dec_huffman_lookup(16#33, 16#2) -> {more, 16#4d, 16#0a}; -dec_huffman_lookup(16#33, 16#3) -> {more, 16#4d, 16#0f}; -dec_huffman_lookup(16#33, 16#4) -> {more, 16#4d, 16#18}; -dec_huffman_lookup(16#33, 16#5) -> {more, 16#4d, 16#1f}; -dec_huffman_lookup(16#33, 16#6) -> {more, 16#4d, 16#29}; -dec_huffman_lookup(16#33, 16#7) -> {ok, 16#4d, 16#38}; -dec_huffman_lookup(16#33, 16#8) -> {more, 16#4e, 16#03}; -dec_huffman_lookup(16#33, 16#9) -> {more, 16#4e, 16#06}; -dec_huffman_lookup(16#33, 16#a) -> {more, 16#4e, 16#0a}; -dec_huffman_lookup(16#33, 16#b) -> {more, 16#4e, 16#0f}; -dec_huffman_lookup(16#33, 16#c) -> {more, 16#4e, 16#18}; -dec_huffman_lookup(16#33, 16#d) -> {more, 16#4e, 16#1f}; -dec_huffman_lookup(16#33, 16#e) -> {more, 16#4e, 16#29}; -dec_huffman_lookup(16#33, 16#f) -> {ok, 16#4e, 16#38}; -dec_huffman_lookup(16#34, 16#0) -> {more, 16#4f, 16#03}; -dec_huffman_lookup(16#34, 16#1) -> {more, 16#4f, 16#06}; -dec_huffman_lookup(16#34, 16#2) -> {more, 16#4f, 16#0a}; -dec_huffman_lookup(16#34, 16#3) -> {more, 16#4f, 16#0f}; -dec_huffman_lookup(16#34, 16#4) -> {more, 16#4f, 16#18}; -dec_huffman_lookup(16#34, 16#5) -> {more, 16#4f, 16#1f}; -dec_huffman_lookup(16#34, 16#6) -> {more, 16#4f, 16#29}; -dec_huffman_lookup(16#34, 16#7) -> {ok, 16#4f, 16#38}; -dec_huffman_lookup(16#34, 16#8) -> {more, 16#50, 16#03}; -dec_huffman_lookup(16#34, 16#9) -> {more, 16#50, 16#06}; -dec_huffman_lookup(16#34, 16#a) -> {more, 16#50, 16#0a}; -dec_huffman_lookup(16#34, 16#b) -> {more, 16#50, 16#0f}; -dec_huffman_lookup(16#34, 16#c) -> {more, 16#50, 16#18}; -dec_huffman_lookup(16#34, 16#d) -> {more, 16#50, 16#1f}; -dec_huffman_lookup(16#34, 16#e) -> {more, 16#50, 16#29}; -dec_huffman_lookup(16#34, 16#f) -> {ok, 16#50, 16#38}; -dec_huffman_lookup(16#35, 16#0) -> {more, 16#51, 16#02}; -dec_huffman_lookup(16#35, 16#1) -> {more, 16#51, 16#09}; -dec_huffman_lookup(16#35, 16#2) -> {more, 16#51, 16#17}; -dec_huffman_lookup(16#35, 16#3) -> {ok, 16#51, 16#28}; -dec_huffman_lookup(16#35, 16#4) -> {more, 16#52, 16#02}; -dec_huffman_lookup(16#35, 16#5) -> {more, 16#52, 16#09}; -dec_huffman_lookup(16#35, 16#6) -> {more, 16#52, 16#17}; -dec_huffman_lookup(16#35, 16#7) -> {ok, 16#52, 16#28}; -dec_huffman_lookup(16#35, 16#8) -> {more, 16#53, 16#02}; -dec_huffman_lookup(16#35, 16#9) -> {more, 16#53, 16#09}; -dec_huffman_lookup(16#35, 16#a) -> {more, 16#53, 16#17}; -dec_huffman_lookup(16#35, 16#b) -> {ok, 16#53, 16#28}; -dec_huffman_lookup(16#35, 16#c) -> {more, 16#54, 16#02}; -dec_huffman_lookup(16#35, 16#d) -> {more, 16#54, 16#09}; -dec_huffman_lookup(16#35, 16#e) -> {more, 16#54, 16#17}; -dec_huffman_lookup(16#35, 16#f) -> {ok, 16#54, 16#28}; -dec_huffman_lookup(16#36, 16#0) -> {more, 16#51, 16#03}; -dec_huffman_lookup(16#36, 16#1) -> {more, 16#51, 16#06}; -dec_huffman_lookup(16#36, 16#2) -> {more, 16#51, 16#0a}; -dec_huffman_lookup(16#36, 16#3) -> {more, 16#51, 16#0f}; -dec_huffman_lookup(16#36, 16#4) -> {more, 16#51, 16#18}; -dec_huffman_lookup(16#36, 16#5) -> {more, 16#51, 16#1f}; -dec_huffman_lookup(16#36, 16#6) -> {more, 16#51, 16#29}; -dec_huffman_lookup(16#36, 16#7) -> {ok, 16#51, 16#38}; -dec_huffman_lookup(16#36, 16#8) -> {more, 16#52, 16#03}; -dec_huffman_lookup(16#36, 16#9) -> {more, 16#52, 16#06}; -dec_huffman_lookup(16#36, 16#a) -> {more, 16#52, 16#0a}; -dec_huffman_lookup(16#36, 16#b) -> {more, 16#52, 16#0f}; -dec_huffman_lookup(16#36, 16#c) -> {more, 16#52, 16#18}; -dec_huffman_lookup(16#36, 16#d) -> {more, 16#52, 16#1f}; -dec_huffman_lookup(16#36, 16#e) -> {more, 16#52, 16#29}; -dec_huffman_lookup(16#36, 16#f) -> {ok, 16#52, 16#38}; -dec_huffman_lookup(16#37, 16#0) -> {more, 16#53, 16#03}; -dec_huffman_lookup(16#37, 16#1) -> {more, 16#53, 16#06}; -dec_huffman_lookup(16#37, 16#2) -> {more, 16#53, 16#0a}; -dec_huffman_lookup(16#37, 16#3) -> {more, 16#53, 16#0f}; -dec_huffman_lookup(16#37, 16#4) -> {more, 16#53, 16#18}; -dec_huffman_lookup(16#37, 16#5) -> {more, 16#53, 16#1f}; -dec_huffman_lookup(16#37, 16#6) -> {more, 16#53, 16#29}; -dec_huffman_lookup(16#37, 16#7) -> {ok, 16#53, 16#38}; -dec_huffman_lookup(16#37, 16#8) -> {more, 16#54, 16#03}; -dec_huffman_lookup(16#37, 16#9) -> {more, 16#54, 16#06}; -dec_huffman_lookup(16#37, 16#a) -> {more, 16#54, 16#0a}; -dec_huffman_lookup(16#37, 16#b) -> {more, 16#54, 16#0f}; -dec_huffman_lookup(16#37, 16#c) -> {more, 16#54, 16#18}; -dec_huffman_lookup(16#37, 16#d) -> {more, 16#54, 16#1f}; -dec_huffman_lookup(16#37, 16#e) -> {more, 16#54, 16#29}; -dec_huffman_lookup(16#37, 16#f) -> {ok, 16#54, 16#38}; -dec_huffman_lookup(16#38, 16#0) -> {ok, 16#55, 16#00}; -dec_huffman_lookup(16#38, 16#1) -> {ok, 16#56, 16#00}; -dec_huffman_lookup(16#38, 16#2) -> {ok, 16#57, 16#00}; -dec_huffman_lookup(16#38, 16#3) -> {ok, 16#59, 16#00}; -dec_huffman_lookup(16#38, 16#4) -> {ok, 16#6a, 16#00}; -dec_huffman_lookup(16#38, 16#5) -> {ok, 16#6b, 16#00}; -dec_huffman_lookup(16#38, 16#6) -> {ok, 16#71, 16#00}; -dec_huffman_lookup(16#38, 16#7) -> {ok, 16#76, 16#00}; -dec_huffman_lookup(16#38, 16#8) -> {ok, 16#77, 16#00}; -dec_huffman_lookup(16#38, 16#9) -> {ok, 16#78, 16#00}; -dec_huffman_lookup(16#38, 16#a) -> {ok, 16#79, 16#00}; -dec_huffman_lookup(16#38, 16#b) -> {ok, 16#7a, 16#00}; -dec_huffman_lookup(16#38, 16#c) -> {more, undefined, 16#46}; -dec_huffman_lookup(16#38, 16#d) -> {more, undefined, 16#47}; -dec_huffman_lookup(16#38, 16#e) -> {more, undefined, 16#49}; -dec_huffman_lookup(16#38, 16#f) -> {ok, undefined, 16#4a}; -dec_huffman_lookup(16#39, 16#0) -> {more, 16#55, 16#01}; -dec_huffman_lookup(16#39, 16#1) -> {ok, 16#55, 16#16}; -dec_huffman_lookup(16#39, 16#2) -> {more, 16#56, 16#01}; -dec_huffman_lookup(16#39, 16#3) -> {ok, 16#56, 16#16}; -dec_huffman_lookup(16#39, 16#4) -> {more, 16#57, 16#01}; -dec_huffman_lookup(16#39, 16#5) -> {ok, 16#57, 16#16}; -dec_huffman_lookup(16#39, 16#6) -> {more, 16#59, 16#01}; -dec_huffman_lookup(16#39, 16#7) -> {ok, 16#59, 16#16}; -dec_huffman_lookup(16#39, 16#8) -> {more, 16#6a, 16#01}; -dec_huffman_lookup(16#39, 16#9) -> {ok, 16#6a, 16#16}; -dec_huffman_lookup(16#39, 16#a) -> {more, 16#6b, 16#01}; -dec_huffman_lookup(16#39, 16#b) -> {ok, 16#6b, 16#16}; -dec_huffman_lookup(16#39, 16#c) -> {more, 16#71, 16#01}; -dec_huffman_lookup(16#39, 16#d) -> {ok, 16#71, 16#16}; -dec_huffman_lookup(16#39, 16#e) -> {more, 16#76, 16#01}; -dec_huffman_lookup(16#39, 16#f) -> {ok, 16#76, 16#16}; -dec_huffman_lookup(16#3a, 16#0) -> {more, 16#55, 16#02}; -dec_huffman_lookup(16#3a, 16#1) -> {more, 16#55, 16#09}; -dec_huffman_lookup(16#3a, 16#2) -> {more, 16#55, 16#17}; -dec_huffman_lookup(16#3a, 16#3) -> {ok, 16#55, 16#28}; -dec_huffman_lookup(16#3a, 16#4) -> {more, 16#56, 16#02}; -dec_huffman_lookup(16#3a, 16#5) -> {more, 16#56, 16#09}; -dec_huffman_lookup(16#3a, 16#6) -> {more, 16#56, 16#17}; -dec_huffman_lookup(16#3a, 16#7) -> {ok, 16#56, 16#28}; -dec_huffman_lookup(16#3a, 16#8) -> {more, 16#57, 16#02}; -dec_huffman_lookup(16#3a, 16#9) -> {more, 16#57, 16#09}; -dec_huffman_lookup(16#3a, 16#a) -> {more, 16#57, 16#17}; -dec_huffman_lookup(16#3a, 16#b) -> {ok, 16#57, 16#28}; -dec_huffman_lookup(16#3a, 16#c) -> {more, 16#59, 16#02}; -dec_huffman_lookup(16#3a, 16#d) -> {more, 16#59, 16#09}; -dec_huffman_lookup(16#3a, 16#e) -> {more, 16#59, 16#17}; -dec_huffman_lookup(16#3a, 16#f) -> {ok, 16#59, 16#28}; -dec_huffman_lookup(16#3b, 16#0) -> {more, 16#55, 16#03}; -dec_huffman_lookup(16#3b, 16#1) -> {more, 16#55, 16#06}; -dec_huffman_lookup(16#3b, 16#2) -> {more, 16#55, 16#0a}; -dec_huffman_lookup(16#3b, 16#3) -> {more, 16#55, 16#0f}; -dec_huffman_lookup(16#3b, 16#4) -> {more, 16#55, 16#18}; -dec_huffman_lookup(16#3b, 16#5) -> {more, 16#55, 16#1f}; -dec_huffman_lookup(16#3b, 16#6) -> {more, 16#55, 16#29}; -dec_huffman_lookup(16#3b, 16#7) -> {ok, 16#55, 16#38}; -dec_huffman_lookup(16#3b, 16#8) -> {more, 16#56, 16#03}; -dec_huffman_lookup(16#3b, 16#9) -> {more, 16#56, 16#06}; -dec_huffman_lookup(16#3b, 16#a) -> {more, 16#56, 16#0a}; -dec_huffman_lookup(16#3b, 16#b) -> {more, 16#56, 16#0f}; -dec_huffman_lookup(16#3b, 16#c) -> {more, 16#56, 16#18}; -dec_huffman_lookup(16#3b, 16#d) -> {more, 16#56, 16#1f}; -dec_huffman_lookup(16#3b, 16#e) -> {more, 16#56, 16#29}; -dec_huffman_lookup(16#3b, 16#f) -> {ok, 16#56, 16#38}; -dec_huffman_lookup(16#3c, 16#0) -> {more, 16#57, 16#03}; -dec_huffman_lookup(16#3c, 16#1) -> {more, 16#57, 16#06}; -dec_huffman_lookup(16#3c, 16#2) -> {more, 16#57, 16#0a}; -dec_huffman_lookup(16#3c, 16#3) -> {more, 16#57, 16#0f}; -dec_huffman_lookup(16#3c, 16#4) -> {more, 16#57, 16#18}; -dec_huffman_lookup(16#3c, 16#5) -> {more, 16#57, 16#1f}; -dec_huffman_lookup(16#3c, 16#6) -> {more, 16#57, 16#29}; -dec_huffman_lookup(16#3c, 16#7) -> {ok, 16#57, 16#38}; -dec_huffman_lookup(16#3c, 16#8) -> {more, 16#59, 16#03}; -dec_huffman_lookup(16#3c, 16#9) -> {more, 16#59, 16#06}; -dec_huffman_lookup(16#3c, 16#a) -> {more, 16#59, 16#0a}; -dec_huffman_lookup(16#3c, 16#b) -> {more, 16#59, 16#0f}; -dec_huffman_lookup(16#3c, 16#c) -> {more, 16#59, 16#18}; -dec_huffman_lookup(16#3c, 16#d) -> {more, 16#59, 16#1f}; -dec_huffman_lookup(16#3c, 16#e) -> {more, 16#59, 16#29}; -dec_huffman_lookup(16#3c, 16#f) -> {ok, 16#59, 16#38}; -dec_huffman_lookup(16#3d, 16#0) -> {more, 16#6a, 16#02}; -dec_huffman_lookup(16#3d, 16#1) -> {more, 16#6a, 16#09}; -dec_huffman_lookup(16#3d, 16#2) -> {more, 16#6a, 16#17}; -dec_huffman_lookup(16#3d, 16#3) -> {ok, 16#6a, 16#28}; -dec_huffman_lookup(16#3d, 16#4) -> {more, 16#6b, 16#02}; -dec_huffman_lookup(16#3d, 16#5) -> {more, 16#6b, 16#09}; -dec_huffman_lookup(16#3d, 16#6) -> {more, 16#6b, 16#17}; -dec_huffman_lookup(16#3d, 16#7) -> {ok, 16#6b, 16#28}; -dec_huffman_lookup(16#3d, 16#8) -> {more, 16#71, 16#02}; -dec_huffman_lookup(16#3d, 16#9) -> {more, 16#71, 16#09}; -dec_huffman_lookup(16#3d, 16#a) -> {more, 16#71, 16#17}; -dec_huffman_lookup(16#3d, 16#b) -> {ok, 16#71, 16#28}; -dec_huffman_lookup(16#3d, 16#c) -> {more, 16#76, 16#02}; -dec_huffman_lookup(16#3d, 16#d) -> {more, 16#76, 16#09}; -dec_huffman_lookup(16#3d, 16#e) -> {more, 16#76, 16#17}; -dec_huffman_lookup(16#3d, 16#f) -> {ok, 16#76, 16#28}; -dec_huffman_lookup(16#3e, 16#0) -> {more, 16#6a, 16#03}; -dec_huffman_lookup(16#3e, 16#1) -> {more, 16#6a, 16#06}; -dec_huffman_lookup(16#3e, 16#2) -> {more, 16#6a, 16#0a}; -dec_huffman_lookup(16#3e, 16#3) -> {more, 16#6a, 16#0f}; -dec_huffman_lookup(16#3e, 16#4) -> {more, 16#6a, 16#18}; -dec_huffman_lookup(16#3e, 16#5) -> {more, 16#6a, 16#1f}; -dec_huffman_lookup(16#3e, 16#6) -> {more, 16#6a, 16#29}; -dec_huffman_lookup(16#3e, 16#7) -> {ok, 16#6a, 16#38}; -dec_huffman_lookup(16#3e, 16#8) -> {more, 16#6b, 16#03}; -dec_huffman_lookup(16#3e, 16#9) -> {more, 16#6b, 16#06}; -dec_huffman_lookup(16#3e, 16#a) -> {more, 16#6b, 16#0a}; -dec_huffman_lookup(16#3e, 16#b) -> {more, 16#6b, 16#0f}; -dec_huffman_lookup(16#3e, 16#c) -> {more, 16#6b, 16#18}; -dec_huffman_lookup(16#3e, 16#d) -> {more, 16#6b, 16#1f}; -dec_huffman_lookup(16#3e, 16#e) -> {more, 16#6b, 16#29}; -dec_huffman_lookup(16#3e, 16#f) -> {ok, 16#6b, 16#38}; -dec_huffman_lookup(16#3f, 16#0) -> {more, 16#71, 16#03}; -dec_huffman_lookup(16#3f, 16#1) -> {more, 16#71, 16#06}; -dec_huffman_lookup(16#3f, 16#2) -> {more, 16#71, 16#0a}; -dec_huffman_lookup(16#3f, 16#3) -> {more, 16#71, 16#0f}; -dec_huffman_lookup(16#3f, 16#4) -> {more, 16#71, 16#18}; -dec_huffman_lookup(16#3f, 16#5) -> {more, 16#71, 16#1f}; -dec_huffman_lookup(16#3f, 16#6) -> {more, 16#71, 16#29}; -dec_huffman_lookup(16#3f, 16#7) -> {ok, 16#71, 16#38}; -dec_huffman_lookup(16#3f, 16#8) -> {more, 16#76, 16#03}; -dec_huffman_lookup(16#3f, 16#9) -> {more, 16#76, 16#06}; -dec_huffman_lookup(16#3f, 16#a) -> {more, 16#76, 16#0a}; -dec_huffman_lookup(16#3f, 16#b) -> {more, 16#76, 16#0f}; -dec_huffman_lookup(16#3f, 16#c) -> {more, 16#76, 16#18}; -dec_huffman_lookup(16#3f, 16#d) -> {more, 16#76, 16#1f}; -dec_huffman_lookup(16#3f, 16#e) -> {more, 16#76, 16#29}; -dec_huffman_lookup(16#3f, 16#f) -> {ok, 16#76, 16#38}; -dec_huffman_lookup(16#40, 16#0) -> {more, 16#77, 16#01}; -dec_huffman_lookup(16#40, 16#1) -> {ok, 16#77, 16#16}; -dec_huffman_lookup(16#40, 16#2) -> {more, 16#78, 16#01}; -dec_huffman_lookup(16#40, 16#3) -> {ok, 16#78, 16#16}; -dec_huffman_lookup(16#40, 16#4) -> {more, 16#79, 16#01}; -dec_huffman_lookup(16#40, 16#5) -> {ok, 16#79, 16#16}; -dec_huffman_lookup(16#40, 16#6) -> {more, 16#7a, 16#01}; -dec_huffman_lookup(16#40, 16#7) -> {ok, 16#7a, 16#16}; -dec_huffman_lookup(16#40, 16#8) -> {ok, 16#26, 16#00}; -dec_huffman_lookup(16#40, 16#9) -> {ok, 16#2a, 16#00}; -dec_huffman_lookup(16#40, 16#a) -> {ok, 16#2c, 16#00}; -dec_huffman_lookup(16#40, 16#b) -> {ok, 16#3b, 16#00}; -dec_huffman_lookup(16#40, 16#c) -> {ok, 16#58, 16#00}; -dec_huffman_lookup(16#40, 16#d) -> {ok, 16#5a, 16#00}; -dec_huffman_lookup(16#40, 16#e) -> {more, undefined, 16#4b}; -dec_huffman_lookup(16#40, 16#f) -> {ok, undefined, 16#4e}; -dec_huffman_lookup(16#41, 16#0) -> {more, 16#77, 16#02}; -dec_huffman_lookup(16#41, 16#1) -> {more, 16#77, 16#09}; -dec_huffman_lookup(16#41, 16#2) -> {more, 16#77, 16#17}; -dec_huffman_lookup(16#41, 16#3) -> {ok, 16#77, 16#28}; -dec_huffman_lookup(16#41, 16#4) -> {more, 16#78, 16#02}; -dec_huffman_lookup(16#41, 16#5) -> {more, 16#78, 16#09}; -dec_huffman_lookup(16#41, 16#6) -> {more, 16#78, 16#17}; -dec_huffman_lookup(16#41, 16#7) -> {ok, 16#78, 16#28}; -dec_huffman_lookup(16#41, 16#8) -> {more, 16#79, 16#02}; -dec_huffman_lookup(16#41, 16#9) -> {more, 16#79, 16#09}; -dec_huffman_lookup(16#41, 16#a) -> {more, 16#79, 16#17}; -dec_huffman_lookup(16#41, 16#b) -> {ok, 16#79, 16#28}; -dec_huffman_lookup(16#41, 16#c) -> {more, 16#7a, 16#02}; -dec_huffman_lookup(16#41, 16#d) -> {more, 16#7a, 16#09}; -dec_huffman_lookup(16#41, 16#e) -> {more, 16#7a, 16#17}; -dec_huffman_lookup(16#41, 16#f) -> {ok, 16#7a, 16#28}; -dec_huffman_lookup(16#42, 16#0) -> {more, 16#77, 16#03}; -dec_huffman_lookup(16#42, 16#1) -> {more, 16#77, 16#06}; -dec_huffman_lookup(16#42, 16#2) -> {more, 16#77, 16#0a}; -dec_huffman_lookup(16#42, 16#3) -> {more, 16#77, 16#0f}; -dec_huffman_lookup(16#42, 16#4) -> {more, 16#77, 16#18}; -dec_huffman_lookup(16#42, 16#5) -> {more, 16#77, 16#1f}; -dec_huffman_lookup(16#42, 16#6) -> {more, 16#77, 16#29}; -dec_huffman_lookup(16#42, 16#7) -> {ok, 16#77, 16#38}; -dec_huffman_lookup(16#42, 16#8) -> {more, 16#78, 16#03}; -dec_huffman_lookup(16#42, 16#9) -> {more, 16#78, 16#06}; -dec_huffman_lookup(16#42, 16#a) -> {more, 16#78, 16#0a}; -dec_huffman_lookup(16#42, 16#b) -> {more, 16#78, 16#0f}; -dec_huffman_lookup(16#42, 16#c) -> {more, 16#78, 16#18}; -dec_huffman_lookup(16#42, 16#d) -> {more, 16#78, 16#1f}; -dec_huffman_lookup(16#42, 16#e) -> {more, 16#78, 16#29}; -dec_huffman_lookup(16#42, 16#f) -> {ok, 16#78, 16#38}; -dec_huffman_lookup(16#43, 16#0) -> {more, 16#79, 16#03}; -dec_huffman_lookup(16#43, 16#1) -> {more, 16#79, 16#06}; -dec_huffman_lookup(16#43, 16#2) -> {more, 16#79, 16#0a}; -dec_huffman_lookup(16#43, 16#3) -> {more, 16#79, 16#0f}; -dec_huffman_lookup(16#43, 16#4) -> {more, 16#79, 16#18}; -dec_huffman_lookup(16#43, 16#5) -> {more, 16#79, 16#1f}; -dec_huffman_lookup(16#43, 16#6) -> {more, 16#79, 16#29}; -dec_huffman_lookup(16#43, 16#7) -> {ok, 16#79, 16#38}; -dec_huffman_lookup(16#43, 16#8) -> {more, 16#7a, 16#03}; -dec_huffman_lookup(16#43, 16#9) -> {more, 16#7a, 16#06}; -dec_huffman_lookup(16#43, 16#a) -> {more, 16#7a, 16#0a}; -dec_huffman_lookup(16#43, 16#b) -> {more, 16#7a, 16#0f}; -dec_huffman_lookup(16#43, 16#c) -> {more, 16#7a, 16#18}; -dec_huffman_lookup(16#43, 16#d) -> {more, 16#7a, 16#1f}; -dec_huffman_lookup(16#43, 16#e) -> {more, 16#7a, 16#29}; -dec_huffman_lookup(16#43, 16#f) -> {ok, 16#7a, 16#38}; -dec_huffman_lookup(16#44, 16#0) -> {more, 16#26, 16#01}; -dec_huffman_lookup(16#44, 16#1) -> {ok, 16#26, 16#16}; -dec_huffman_lookup(16#44, 16#2) -> {more, 16#2a, 16#01}; -dec_huffman_lookup(16#44, 16#3) -> {ok, 16#2a, 16#16}; -dec_huffman_lookup(16#44, 16#4) -> {more, 16#2c, 16#01}; -dec_huffman_lookup(16#44, 16#5) -> {ok, 16#2c, 16#16}; -dec_huffman_lookup(16#44, 16#6) -> {more, 16#3b, 16#01}; -dec_huffman_lookup(16#44, 16#7) -> {ok, 16#3b, 16#16}; -dec_huffman_lookup(16#44, 16#8) -> {more, 16#58, 16#01}; -dec_huffman_lookup(16#44, 16#9) -> {ok, 16#58, 16#16}; -dec_huffman_lookup(16#44, 16#a) -> {more, 16#5a, 16#01}; -dec_huffman_lookup(16#44, 16#b) -> {ok, 16#5a, 16#16}; -dec_huffman_lookup(16#44, 16#c) -> {more, undefined, 16#4c}; -dec_huffman_lookup(16#44, 16#d) -> {more, undefined, 16#4d}; -dec_huffman_lookup(16#44, 16#e) -> {more, undefined, 16#4f}; -dec_huffman_lookup(16#44, 16#f) -> {ok, undefined, 16#51}; -dec_huffman_lookup(16#45, 16#0) -> {more, 16#26, 16#02}; -dec_huffman_lookup(16#45, 16#1) -> {more, 16#26, 16#09}; -dec_huffman_lookup(16#45, 16#2) -> {more, 16#26, 16#17}; -dec_huffman_lookup(16#45, 16#3) -> {ok, 16#26, 16#28}; -dec_huffman_lookup(16#45, 16#4) -> {more, 16#2a, 16#02}; -dec_huffman_lookup(16#45, 16#5) -> {more, 16#2a, 16#09}; -dec_huffman_lookup(16#45, 16#6) -> {more, 16#2a, 16#17}; -dec_huffman_lookup(16#45, 16#7) -> {ok, 16#2a, 16#28}; -dec_huffman_lookup(16#45, 16#8) -> {more, 16#2c, 16#02}; -dec_huffman_lookup(16#45, 16#9) -> {more, 16#2c, 16#09}; -dec_huffman_lookup(16#45, 16#a) -> {more, 16#2c, 16#17}; -dec_huffman_lookup(16#45, 16#b) -> {ok, 16#2c, 16#28}; -dec_huffman_lookup(16#45, 16#c) -> {more, 16#3b, 16#02}; -dec_huffman_lookup(16#45, 16#d) -> {more, 16#3b, 16#09}; -dec_huffman_lookup(16#45, 16#e) -> {more, 16#3b, 16#17}; -dec_huffman_lookup(16#45, 16#f) -> {ok, 16#3b, 16#28}; -dec_huffman_lookup(16#46, 16#0) -> {more, 16#26, 16#03}; -dec_huffman_lookup(16#46, 16#1) -> {more, 16#26, 16#06}; -dec_huffman_lookup(16#46, 16#2) -> {more, 16#26, 16#0a}; -dec_huffman_lookup(16#46, 16#3) -> {more, 16#26, 16#0f}; -dec_huffman_lookup(16#46, 16#4) -> {more, 16#26, 16#18}; -dec_huffman_lookup(16#46, 16#5) -> {more, 16#26, 16#1f}; -dec_huffman_lookup(16#46, 16#6) -> {more, 16#26, 16#29}; -dec_huffman_lookup(16#46, 16#7) -> {ok, 16#26, 16#38}; -dec_huffman_lookup(16#46, 16#8) -> {more, 16#2a, 16#03}; -dec_huffman_lookup(16#46, 16#9) -> {more, 16#2a, 16#06}; -dec_huffman_lookup(16#46, 16#a) -> {more, 16#2a, 16#0a}; -dec_huffman_lookup(16#46, 16#b) -> {more, 16#2a, 16#0f}; -dec_huffman_lookup(16#46, 16#c) -> {more, 16#2a, 16#18}; -dec_huffman_lookup(16#46, 16#d) -> {more, 16#2a, 16#1f}; -dec_huffman_lookup(16#46, 16#e) -> {more, 16#2a, 16#29}; -dec_huffman_lookup(16#46, 16#f) -> {ok, 16#2a, 16#38}; -dec_huffman_lookup(16#47, 16#0) -> {more, 16#2c, 16#03}; -dec_huffman_lookup(16#47, 16#1) -> {more, 16#2c, 16#06}; -dec_huffman_lookup(16#47, 16#2) -> {more, 16#2c, 16#0a}; -dec_huffman_lookup(16#47, 16#3) -> {more, 16#2c, 16#0f}; -dec_huffman_lookup(16#47, 16#4) -> {more, 16#2c, 16#18}; -dec_huffman_lookup(16#47, 16#5) -> {more, 16#2c, 16#1f}; -dec_huffman_lookup(16#47, 16#6) -> {more, 16#2c, 16#29}; -dec_huffman_lookup(16#47, 16#7) -> {ok, 16#2c, 16#38}; -dec_huffman_lookup(16#47, 16#8) -> {more, 16#3b, 16#03}; -dec_huffman_lookup(16#47, 16#9) -> {more, 16#3b, 16#06}; -dec_huffman_lookup(16#47, 16#a) -> {more, 16#3b, 16#0a}; -dec_huffman_lookup(16#47, 16#b) -> {more, 16#3b, 16#0f}; -dec_huffman_lookup(16#47, 16#c) -> {more, 16#3b, 16#18}; -dec_huffman_lookup(16#47, 16#d) -> {more, 16#3b, 16#1f}; -dec_huffman_lookup(16#47, 16#e) -> {more, 16#3b, 16#29}; -dec_huffman_lookup(16#47, 16#f) -> {ok, 16#3b, 16#38}; -dec_huffman_lookup(16#48, 16#0) -> {more, 16#58, 16#02}; -dec_huffman_lookup(16#48, 16#1) -> {more, 16#58, 16#09}; -dec_huffman_lookup(16#48, 16#2) -> {more, 16#58, 16#17}; -dec_huffman_lookup(16#48, 16#3) -> {ok, 16#58, 16#28}; -dec_huffman_lookup(16#48, 16#4) -> {more, 16#5a, 16#02}; -dec_huffman_lookup(16#48, 16#5) -> {more, 16#5a, 16#09}; -dec_huffman_lookup(16#48, 16#6) -> {more, 16#5a, 16#17}; -dec_huffman_lookup(16#48, 16#7) -> {ok, 16#5a, 16#28}; -dec_huffman_lookup(16#48, 16#8) -> {ok, 16#21, 16#00}; -dec_huffman_lookup(16#48, 16#9) -> {ok, 16#22, 16#00}; -dec_huffman_lookup(16#48, 16#a) -> {ok, 16#28, 16#00}; -dec_huffman_lookup(16#48, 16#b) -> {ok, 16#29, 16#00}; -dec_huffman_lookup(16#48, 16#c) -> {ok, 16#3f, 16#00}; -dec_huffman_lookup(16#48, 16#d) -> {more, undefined, 16#50}; -dec_huffman_lookup(16#48, 16#e) -> {more, undefined, 16#52}; -dec_huffman_lookup(16#48, 16#f) -> {ok, undefined, 16#54}; -dec_huffman_lookup(16#49, 16#0) -> {more, 16#58, 16#03}; -dec_huffman_lookup(16#49, 16#1) -> {more, 16#58, 16#06}; -dec_huffman_lookup(16#49, 16#2) -> {more, 16#58, 16#0a}; -dec_huffman_lookup(16#49, 16#3) -> {more, 16#58, 16#0f}; -dec_huffman_lookup(16#49, 16#4) -> {more, 16#58, 16#18}; -dec_huffman_lookup(16#49, 16#5) -> {more, 16#58, 16#1f}; -dec_huffman_lookup(16#49, 16#6) -> {more, 16#58, 16#29}; -dec_huffman_lookup(16#49, 16#7) -> {ok, 16#58, 16#38}; -dec_huffman_lookup(16#49, 16#8) -> {more, 16#5a, 16#03}; -dec_huffman_lookup(16#49, 16#9) -> {more, 16#5a, 16#06}; -dec_huffman_lookup(16#49, 16#a) -> {more, 16#5a, 16#0a}; -dec_huffman_lookup(16#49, 16#b) -> {more, 16#5a, 16#0f}; -dec_huffman_lookup(16#49, 16#c) -> {more, 16#5a, 16#18}; -dec_huffman_lookup(16#49, 16#d) -> {more, 16#5a, 16#1f}; -dec_huffman_lookup(16#49, 16#e) -> {more, 16#5a, 16#29}; -dec_huffman_lookup(16#49, 16#f) -> {ok, 16#5a, 16#38}; -dec_huffman_lookup(16#4a, 16#0) -> {more, 16#21, 16#01}; -dec_huffman_lookup(16#4a, 16#1) -> {ok, 16#21, 16#16}; -dec_huffman_lookup(16#4a, 16#2) -> {more, 16#22, 16#01}; -dec_huffman_lookup(16#4a, 16#3) -> {ok, 16#22, 16#16}; -dec_huffman_lookup(16#4a, 16#4) -> {more, 16#28, 16#01}; -dec_huffman_lookup(16#4a, 16#5) -> {ok, 16#28, 16#16}; -dec_huffman_lookup(16#4a, 16#6) -> {more, 16#29, 16#01}; -dec_huffman_lookup(16#4a, 16#7) -> {ok, 16#29, 16#16}; -dec_huffman_lookup(16#4a, 16#8) -> {more, 16#3f, 16#01}; -dec_huffman_lookup(16#4a, 16#9) -> {ok, 16#3f, 16#16}; -dec_huffman_lookup(16#4a, 16#a) -> {ok, 16#27, 16#00}; -dec_huffman_lookup(16#4a, 16#b) -> {ok, 16#2b, 16#00}; -dec_huffman_lookup(16#4a, 16#c) -> {ok, 16#7c, 16#00}; -dec_huffman_lookup(16#4a, 16#d) -> {more, undefined, 16#53}; -dec_huffman_lookup(16#4a, 16#e) -> {more, undefined, 16#55}; -dec_huffman_lookup(16#4a, 16#f) -> {ok, undefined, 16#58}; -dec_huffman_lookup(16#4b, 16#0) -> {more, 16#21, 16#02}; -dec_huffman_lookup(16#4b, 16#1) -> {more, 16#21, 16#09}; -dec_huffman_lookup(16#4b, 16#2) -> {more, 16#21, 16#17}; -dec_huffman_lookup(16#4b, 16#3) -> {ok, 16#21, 16#28}; -dec_huffman_lookup(16#4b, 16#4) -> {more, 16#22, 16#02}; -dec_huffman_lookup(16#4b, 16#5) -> {more, 16#22, 16#09}; -dec_huffman_lookup(16#4b, 16#6) -> {more, 16#22, 16#17}; -dec_huffman_lookup(16#4b, 16#7) -> {ok, 16#22, 16#28}; -dec_huffman_lookup(16#4b, 16#8) -> {more, 16#28, 16#02}; -dec_huffman_lookup(16#4b, 16#9) -> {more, 16#28, 16#09}; -dec_huffman_lookup(16#4b, 16#a) -> {more, 16#28, 16#17}; -dec_huffman_lookup(16#4b, 16#b) -> {ok, 16#28, 16#28}; -dec_huffman_lookup(16#4b, 16#c) -> {more, 16#29, 16#02}; -dec_huffman_lookup(16#4b, 16#d) -> {more, 16#29, 16#09}; -dec_huffman_lookup(16#4b, 16#e) -> {more, 16#29, 16#17}; -dec_huffman_lookup(16#4b, 16#f) -> {ok, 16#29, 16#28}; -dec_huffman_lookup(16#4c, 16#0) -> {more, 16#21, 16#03}; -dec_huffman_lookup(16#4c, 16#1) -> {more, 16#21, 16#06}; -dec_huffman_lookup(16#4c, 16#2) -> {more, 16#21, 16#0a}; -dec_huffman_lookup(16#4c, 16#3) -> {more, 16#21, 16#0f}; -dec_huffman_lookup(16#4c, 16#4) -> {more, 16#21, 16#18}; -dec_huffman_lookup(16#4c, 16#5) -> {more, 16#21, 16#1f}; -dec_huffman_lookup(16#4c, 16#6) -> {more, 16#21, 16#29}; -dec_huffman_lookup(16#4c, 16#7) -> {ok, 16#21, 16#38}; -dec_huffman_lookup(16#4c, 16#8) -> {more, 16#22, 16#03}; -dec_huffman_lookup(16#4c, 16#9) -> {more, 16#22, 16#06}; -dec_huffman_lookup(16#4c, 16#a) -> {more, 16#22, 16#0a}; -dec_huffman_lookup(16#4c, 16#b) -> {more, 16#22, 16#0f}; -dec_huffman_lookup(16#4c, 16#c) -> {more, 16#22, 16#18}; -dec_huffman_lookup(16#4c, 16#d) -> {more, 16#22, 16#1f}; -dec_huffman_lookup(16#4c, 16#e) -> {more, 16#22, 16#29}; -dec_huffman_lookup(16#4c, 16#f) -> {ok, 16#22, 16#38}; -dec_huffman_lookup(16#4d, 16#0) -> {more, 16#28, 16#03}; -dec_huffman_lookup(16#4d, 16#1) -> {more, 16#28, 16#06}; -dec_huffman_lookup(16#4d, 16#2) -> {more, 16#28, 16#0a}; -dec_huffman_lookup(16#4d, 16#3) -> {more, 16#28, 16#0f}; -dec_huffman_lookup(16#4d, 16#4) -> {more, 16#28, 16#18}; -dec_huffman_lookup(16#4d, 16#5) -> {more, 16#28, 16#1f}; -dec_huffman_lookup(16#4d, 16#6) -> {more, 16#28, 16#29}; -dec_huffman_lookup(16#4d, 16#7) -> {ok, 16#28, 16#38}; -dec_huffman_lookup(16#4d, 16#8) -> {more, 16#29, 16#03}; -dec_huffman_lookup(16#4d, 16#9) -> {more, 16#29, 16#06}; -dec_huffman_lookup(16#4d, 16#a) -> {more, 16#29, 16#0a}; -dec_huffman_lookup(16#4d, 16#b) -> {more, 16#29, 16#0f}; -dec_huffman_lookup(16#4d, 16#c) -> {more, 16#29, 16#18}; -dec_huffman_lookup(16#4d, 16#d) -> {more, 16#29, 16#1f}; -dec_huffman_lookup(16#4d, 16#e) -> {more, 16#29, 16#29}; -dec_huffman_lookup(16#4d, 16#f) -> {ok, 16#29, 16#38}; -dec_huffman_lookup(16#4e, 16#0) -> {more, 16#3f, 16#02}; -dec_huffman_lookup(16#4e, 16#1) -> {more, 16#3f, 16#09}; -dec_huffman_lookup(16#4e, 16#2) -> {more, 16#3f, 16#17}; -dec_huffman_lookup(16#4e, 16#3) -> {ok, 16#3f, 16#28}; -dec_huffman_lookup(16#4e, 16#4) -> {more, 16#27, 16#01}; -dec_huffman_lookup(16#4e, 16#5) -> {ok, 16#27, 16#16}; -dec_huffman_lookup(16#4e, 16#6) -> {more, 16#2b, 16#01}; -dec_huffman_lookup(16#4e, 16#7) -> {ok, 16#2b, 16#16}; -dec_huffman_lookup(16#4e, 16#8) -> {more, 16#7c, 16#01}; -dec_huffman_lookup(16#4e, 16#9) -> {ok, 16#7c, 16#16}; -dec_huffman_lookup(16#4e, 16#a) -> {ok, 16#23, 16#00}; -dec_huffman_lookup(16#4e, 16#b) -> {ok, 16#3e, 16#00}; -dec_huffman_lookup(16#4e, 16#c) -> {more, undefined, 16#56}; -dec_huffman_lookup(16#4e, 16#d) -> {more, undefined, 16#57}; -dec_huffman_lookup(16#4e, 16#e) -> {more, undefined, 16#59}; -dec_huffman_lookup(16#4e, 16#f) -> {ok, undefined, 16#5a}; -dec_huffman_lookup(16#4f, 16#0) -> {more, 16#3f, 16#03}; -dec_huffman_lookup(16#4f, 16#1) -> {more, 16#3f, 16#06}; -dec_huffman_lookup(16#4f, 16#2) -> {more, 16#3f, 16#0a}; -dec_huffman_lookup(16#4f, 16#3) -> {more, 16#3f, 16#0f}; -dec_huffman_lookup(16#4f, 16#4) -> {more, 16#3f, 16#18}; -dec_huffman_lookup(16#4f, 16#5) -> {more, 16#3f, 16#1f}; -dec_huffman_lookup(16#4f, 16#6) -> {more, 16#3f, 16#29}; -dec_huffman_lookup(16#4f, 16#7) -> {ok, 16#3f, 16#38}; -dec_huffman_lookup(16#4f, 16#8) -> {more, 16#27, 16#02}; -dec_huffman_lookup(16#4f, 16#9) -> {more, 16#27, 16#09}; -dec_huffman_lookup(16#4f, 16#a) -> {more, 16#27, 16#17}; -dec_huffman_lookup(16#4f, 16#b) -> {ok, 16#27, 16#28}; -dec_huffman_lookup(16#4f, 16#c) -> {more, 16#2b, 16#02}; -dec_huffman_lookup(16#4f, 16#d) -> {more, 16#2b, 16#09}; -dec_huffman_lookup(16#4f, 16#e) -> {more, 16#2b, 16#17}; -dec_huffman_lookup(16#4f, 16#f) -> {ok, 16#2b, 16#28}; -dec_huffman_lookup(16#50, 16#0) -> {more, 16#27, 16#03}; -dec_huffman_lookup(16#50, 16#1) -> {more, 16#27, 16#06}; -dec_huffman_lookup(16#50, 16#2) -> {more, 16#27, 16#0a}; -dec_huffman_lookup(16#50, 16#3) -> {more, 16#27, 16#0f}; -dec_huffman_lookup(16#50, 16#4) -> {more, 16#27, 16#18}; -dec_huffman_lookup(16#50, 16#5) -> {more, 16#27, 16#1f}; -dec_huffman_lookup(16#50, 16#6) -> {more, 16#27, 16#29}; -dec_huffman_lookup(16#50, 16#7) -> {ok, 16#27, 16#38}; -dec_huffman_lookup(16#50, 16#8) -> {more, 16#2b, 16#03}; -dec_huffman_lookup(16#50, 16#9) -> {more, 16#2b, 16#06}; -dec_huffman_lookup(16#50, 16#a) -> {more, 16#2b, 16#0a}; -dec_huffman_lookup(16#50, 16#b) -> {more, 16#2b, 16#0f}; -dec_huffman_lookup(16#50, 16#c) -> {more, 16#2b, 16#18}; -dec_huffman_lookup(16#50, 16#d) -> {more, 16#2b, 16#1f}; -dec_huffman_lookup(16#50, 16#e) -> {more, 16#2b, 16#29}; -dec_huffman_lookup(16#50, 16#f) -> {ok, 16#2b, 16#38}; -dec_huffman_lookup(16#51, 16#0) -> {more, 16#7c, 16#02}; -dec_huffman_lookup(16#51, 16#1) -> {more, 16#7c, 16#09}; -dec_huffman_lookup(16#51, 16#2) -> {more, 16#7c, 16#17}; -dec_huffman_lookup(16#51, 16#3) -> {ok, 16#7c, 16#28}; -dec_huffman_lookup(16#51, 16#4) -> {more, 16#23, 16#01}; -dec_huffman_lookup(16#51, 16#5) -> {ok, 16#23, 16#16}; -dec_huffman_lookup(16#51, 16#6) -> {more, 16#3e, 16#01}; -dec_huffman_lookup(16#51, 16#7) -> {ok, 16#3e, 16#16}; -dec_huffman_lookup(16#51, 16#8) -> {ok, 16#00, 16#00}; -dec_huffman_lookup(16#51, 16#9) -> {ok, 16#24, 16#00}; -dec_huffman_lookup(16#51, 16#a) -> {ok, 16#40, 16#00}; -dec_huffman_lookup(16#51, 16#b) -> {ok, 16#5b, 16#00}; -dec_huffman_lookup(16#51, 16#c) -> {ok, 16#5d, 16#00}; -dec_huffman_lookup(16#51, 16#d) -> {ok, 16#7e, 16#00}; -dec_huffman_lookup(16#51, 16#e) -> {more, undefined, 16#5b}; -dec_huffman_lookup(16#51, 16#f) -> {ok, undefined, 16#5c}; -dec_huffman_lookup(16#52, 16#0) -> {more, 16#7c, 16#03}; -dec_huffman_lookup(16#52, 16#1) -> {more, 16#7c, 16#06}; -dec_huffman_lookup(16#52, 16#2) -> {more, 16#7c, 16#0a}; -dec_huffman_lookup(16#52, 16#3) -> {more, 16#7c, 16#0f}; -dec_huffman_lookup(16#52, 16#4) -> {more, 16#7c, 16#18}; -dec_huffman_lookup(16#52, 16#5) -> {more, 16#7c, 16#1f}; -dec_huffman_lookup(16#52, 16#6) -> {more, 16#7c, 16#29}; -dec_huffman_lookup(16#52, 16#7) -> {ok, 16#7c, 16#38}; -dec_huffman_lookup(16#52, 16#8) -> {more, 16#23, 16#02}; -dec_huffman_lookup(16#52, 16#9) -> {more, 16#23, 16#09}; -dec_huffman_lookup(16#52, 16#a) -> {more, 16#23, 16#17}; -dec_huffman_lookup(16#52, 16#b) -> {ok, 16#23, 16#28}; -dec_huffman_lookup(16#52, 16#c) -> {more, 16#3e, 16#02}; -dec_huffman_lookup(16#52, 16#d) -> {more, 16#3e, 16#09}; -dec_huffman_lookup(16#52, 16#e) -> {more, 16#3e, 16#17}; -dec_huffman_lookup(16#52, 16#f) -> {ok, 16#3e, 16#28}; -dec_huffman_lookup(16#53, 16#0) -> {more, 16#23, 16#03}; -dec_huffman_lookup(16#53, 16#1) -> {more, 16#23, 16#06}; -dec_huffman_lookup(16#53, 16#2) -> {more, 16#23, 16#0a}; -dec_huffman_lookup(16#53, 16#3) -> {more, 16#23, 16#0f}; -dec_huffman_lookup(16#53, 16#4) -> {more, 16#23, 16#18}; -dec_huffman_lookup(16#53, 16#5) -> {more, 16#23, 16#1f}; -dec_huffman_lookup(16#53, 16#6) -> {more, 16#23, 16#29}; -dec_huffman_lookup(16#53, 16#7) -> {ok, 16#23, 16#38}; -dec_huffman_lookup(16#53, 16#8) -> {more, 16#3e, 16#03}; -dec_huffman_lookup(16#53, 16#9) -> {more, 16#3e, 16#06}; -dec_huffman_lookup(16#53, 16#a) -> {more, 16#3e, 16#0a}; -dec_huffman_lookup(16#53, 16#b) -> {more, 16#3e, 16#0f}; -dec_huffman_lookup(16#53, 16#c) -> {more, 16#3e, 16#18}; -dec_huffman_lookup(16#53, 16#d) -> {more, 16#3e, 16#1f}; -dec_huffman_lookup(16#53, 16#e) -> {more, 16#3e, 16#29}; -dec_huffman_lookup(16#53, 16#f) -> {ok, 16#3e, 16#38}; -dec_huffman_lookup(16#54, 16#0) -> {more, 16#00, 16#01}; -dec_huffman_lookup(16#54, 16#1) -> {ok, 16#00, 16#16}; -dec_huffman_lookup(16#54, 16#2) -> {more, 16#24, 16#01}; -dec_huffman_lookup(16#54, 16#3) -> {ok, 16#24, 16#16}; -dec_huffman_lookup(16#54, 16#4) -> {more, 16#40, 16#01}; -dec_huffman_lookup(16#54, 16#5) -> {ok, 16#40, 16#16}; -dec_huffman_lookup(16#54, 16#6) -> {more, 16#5b, 16#01}; -dec_huffman_lookup(16#54, 16#7) -> {ok, 16#5b, 16#16}; -dec_huffman_lookup(16#54, 16#8) -> {more, 16#5d, 16#01}; -dec_huffman_lookup(16#54, 16#9) -> {ok, 16#5d, 16#16}; -dec_huffman_lookup(16#54, 16#a) -> {more, 16#7e, 16#01}; -dec_huffman_lookup(16#54, 16#b) -> {ok, 16#7e, 16#16}; -dec_huffman_lookup(16#54, 16#c) -> {ok, 16#5e, 16#00}; -dec_huffman_lookup(16#54, 16#d) -> {ok, 16#7d, 16#00}; -dec_huffman_lookup(16#54, 16#e) -> {more, undefined, 16#5d}; -dec_huffman_lookup(16#54, 16#f) -> {ok, undefined, 16#5e}; -dec_huffman_lookup(16#55, 16#0) -> {more, 16#00, 16#02}; -dec_huffman_lookup(16#55, 16#1) -> {more, 16#00, 16#09}; -dec_huffman_lookup(16#55, 16#2) -> {more, 16#00, 16#17}; -dec_huffman_lookup(16#55, 16#3) -> {ok, 16#00, 16#28}; -dec_huffman_lookup(16#55, 16#4) -> {more, 16#24, 16#02}; -dec_huffman_lookup(16#55, 16#5) -> {more, 16#24, 16#09}; -dec_huffman_lookup(16#55, 16#6) -> {more, 16#24, 16#17}; -dec_huffman_lookup(16#55, 16#7) -> {ok, 16#24, 16#28}; -dec_huffman_lookup(16#55, 16#8) -> {more, 16#40, 16#02}; -dec_huffman_lookup(16#55, 16#9) -> {more, 16#40, 16#09}; -dec_huffman_lookup(16#55, 16#a) -> {more, 16#40, 16#17}; -dec_huffman_lookup(16#55, 16#b) -> {ok, 16#40, 16#28}; -dec_huffman_lookup(16#55, 16#c) -> {more, 16#5b, 16#02}; -dec_huffman_lookup(16#55, 16#d) -> {more, 16#5b, 16#09}; -dec_huffman_lookup(16#55, 16#e) -> {more, 16#5b, 16#17}; -dec_huffman_lookup(16#55, 16#f) -> {ok, 16#5b, 16#28}; -dec_huffman_lookup(16#56, 16#0) -> {more, 16#00, 16#03}; -dec_huffman_lookup(16#56, 16#1) -> {more, 16#00, 16#06}; -dec_huffman_lookup(16#56, 16#2) -> {more, 16#00, 16#0a}; -dec_huffman_lookup(16#56, 16#3) -> {more, 16#00, 16#0f}; -dec_huffman_lookup(16#56, 16#4) -> {more, 16#00, 16#18}; -dec_huffman_lookup(16#56, 16#5) -> {more, 16#00, 16#1f}; -dec_huffman_lookup(16#56, 16#6) -> {more, 16#00, 16#29}; -dec_huffman_lookup(16#56, 16#7) -> {ok, 16#00, 16#38}; -dec_huffman_lookup(16#56, 16#8) -> {more, 16#24, 16#03}; -dec_huffman_lookup(16#56, 16#9) -> {more, 16#24, 16#06}; -dec_huffman_lookup(16#56, 16#a) -> {more, 16#24, 16#0a}; -dec_huffman_lookup(16#56, 16#b) -> {more, 16#24, 16#0f}; -dec_huffman_lookup(16#56, 16#c) -> {more, 16#24, 16#18}; -dec_huffman_lookup(16#56, 16#d) -> {more, 16#24, 16#1f}; -dec_huffman_lookup(16#56, 16#e) -> {more, 16#24, 16#29}; -dec_huffman_lookup(16#56, 16#f) -> {ok, 16#24, 16#38}; -dec_huffman_lookup(16#57, 16#0) -> {more, 16#40, 16#03}; -dec_huffman_lookup(16#57, 16#1) -> {more, 16#40, 16#06}; -dec_huffman_lookup(16#57, 16#2) -> {more, 16#40, 16#0a}; -dec_huffman_lookup(16#57, 16#3) -> {more, 16#40, 16#0f}; -dec_huffman_lookup(16#57, 16#4) -> {more, 16#40, 16#18}; -dec_huffman_lookup(16#57, 16#5) -> {more, 16#40, 16#1f}; -dec_huffman_lookup(16#57, 16#6) -> {more, 16#40, 16#29}; -dec_huffman_lookup(16#57, 16#7) -> {ok, 16#40, 16#38}; -dec_huffman_lookup(16#57, 16#8) -> {more, 16#5b, 16#03}; -dec_huffman_lookup(16#57, 16#9) -> {more, 16#5b, 16#06}; -dec_huffman_lookup(16#57, 16#a) -> {more, 16#5b, 16#0a}; -dec_huffman_lookup(16#57, 16#b) -> {more, 16#5b, 16#0f}; -dec_huffman_lookup(16#57, 16#c) -> {more, 16#5b, 16#18}; -dec_huffman_lookup(16#57, 16#d) -> {more, 16#5b, 16#1f}; -dec_huffman_lookup(16#57, 16#e) -> {more, 16#5b, 16#29}; -dec_huffman_lookup(16#57, 16#f) -> {ok, 16#5b, 16#38}; -dec_huffman_lookup(16#58, 16#0) -> {more, 16#5d, 16#02}; -dec_huffman_lookup(16#58, 16#1) -> {more, 16#5d, 16#09}; -dec_huffman_lookup(16#58, 16#2) -> {more, 16#5d, 16#17}; -dec_huffman_lookup(16#58, 16#3) -> {ok, 16#5d, 16#28}; -dec_huffman_lookup(16#58, 16#4) -> {more, 16#7e, 16#02}; -dec_huffman_lookup(16#58, 16#5) -> {more, 16#7e, 16#09}; -dec_huffman_lookup(16#58, 16#6) -> {more, 16#7e, 16#17}; -dec_huffman_lookup(16#58, 16#7) -> {ok, 16#7e, 16#28}; -dec_huffman_lookup(16#58, 16#8) -> {more, 16#5e, 16#01}; -dec_huffman_lookup(16#58, 16#9) -> {ok, 16#5e, 16#16}; -dec_huffman_lookup(16#58, 16#a) -> {more, 16#7d, 16#01}; -dec_huffman_lookup(16#58, 16#b) -> {ok, 16#7d, 16#16}; -dec_huffman_lookup(16#58, 16#c) -> {ok, 16#3c, 16#00}; -dec_huffman_lookup(16#58, 16#d) -> {ok, 16#60, 16#00}; -dec_huffman_lookup(16#58, 16#e) -> {ok, 16#7b, 16#00}; -dec_huffman_lookup(16#58, 16#f) -> {ok, undefined, 16#5f}; -dec_huffman_lookup(16#59, 16#0) -> {more, 16#5d, 16#03}; -dec_huffman_lookup(16#59, 16#1) -> {more, 16#5d, 16#06}; -dec_huffman_lookup(16#59, 16#2) -> {more, 16#5d, 16#0a}; -dec_huffman_lookup(16#59, 16#3) -> {more, 16#5d, 16#0f}; -dec_huffman_lookup(16#59, 16#4) -> {more, 16#5d, 16#18}; -dec_huffman_lookup(16#59, 16#5) -> {more, 16#5d, 16#1f}; -dec_huffman_lookup(16#59, 16#6) -> {more, 16#5d, 16#29}; -dec_huffman_lookup(16#59, 16#7) -> {ok, 16#5d, 16#38}; -dec_huffman_lookup(16#59, 16#8) -> {more, 16#7e, 16#03}; -dec_huffman_lookup(16#59, 16#9) -> {more, 16#7e, 16#06}; -dec_huffman_lookup(16#59, 16#a) -> {more, 16#7e, 16#0a}; -dec_huffman_lookup(16#59, 16#b) -> {more, 16#7e, 16#0f}; -dec_huffman_lookup(16#59, 16#c) -> {more, 16#7e, 16#18}; -dec_huffman_lookup(16#59, 16#d) -> {more, 16#7e, 16#1f}; -dec_huffman_lookup(16#59, 16#e) -> {more, 16#7e, 16#29}; -dec_huffman_lookup(16#59, 16#f) -> {ok, 16#7e, 16#38}; -dec_huffman_lookup(16#5a, 16#0) -> {more, 16#5e, 16#02}; -dec_huffman_lookup(16#5a, 16#1) -> {more, 16#5e, 16#09}; -dec_huffman_lookup(16#5a, 16#2) -> {more, 16#5e, 16#17}; -dec_huffman_lookup(16#5a, 16#3) -> {ok, 16#5e, 16#28}; -dec_huffman_lookup(16#5a, 16#4) -> {more, 16#7d, 16#02}; -dec_huffman_lookup(16#5a, 16#5) -> {more, 16#7d, 16#09}; -dec_huffman_lookup(16#5a, 16#6) -> {more, 16#7d, 16#17}; -dec_huffman_lookup(16#5a, 16#7) -> {ok, 16#7d, 16#28}; -dec_huffman_lookup(16#5a, 16#8) -> {more, 16#3c, 16#01}; -dec_huffman_lookup(16#5a, 16#9) -> {ok, 16#3c, 16#16}; -dec_huffman_lookup(16#5a, 16#a) -> {more, 16#60, 16#01}; -dec_huffman_lookup(16#5a, 16#b) -> {ok, 16#60, 16#16}; -dec_huffman_lookup(16#5a, 16#c) -> {more, 16#7b, 16#01}; -dec_huffman_lookup(16#5a, 16#d) -> {ok, 16#7b, 16#16}; -dec_huffman_lookup(16#5a, 16#e) -> {more, undefined, 16#60}; -dec_huffman_lookup(16#5a, 16#f) -> {ok, undefined, 16#6e}; -dec_huffman_lookup(16#5b, 16#0) -> {more, 16#5e, 16#03}; -dec_huffman_lookup(16#5b, 16#1) -> {more, 16#5e, 16#06}; -dec_huffman_lookup(16#5b, 16#2) -> {more, 16#5e, 16#0a}; -dec_huffman_lookup(16#5b, 16#3) -> {more, 16#5e, 16#0f}; -dec_huffman_lookup(16#5b, 16#4) -> {more, 16#5e, 16#18}; -dec_huffman_lookup(16#5b, 16#5) -> {more, 16#5e, 16#1f}; -dec_huffman_lookup(16#5b, 16#6) -> {more, 16#5e, 16#29}; -dec_huffman_lookup(16#5b, 16#7) -> {ok, 16#5e, 16#38}; -dec_huffman_lookup(16#5b, 16#8) -> {more, 16#7d, 16#03}; -dec_huffman_lookup(16#5b, 16#9) -> {more, 16#7d, 16#06}; -dec_huffman_lookup(16#5b, 16#a) -> {more, 16#7d, 16#0a}; -dec_huffman_lookup(16#5b, 16#b) -> {more, 16#7d, 16#0f}; -dec_huffman_lookup(16#5b, 16#c) -> {more, 16#7d, 16#18}; -dec_huffman_lookup(16#5b, 16#d) -> {more, 16#7d, 16#1f}; -dec_huffman_lookup(16#5b, 16#e) -> {more, 16#7d, 16#29}; -dec_huffman_lookup(16#5b, 16#f) -> {ok, 16#7d, 16#38}; -dec_huffman_lookup(16#5c, 16#0) -> {more, 16#3c, 16#02}; -dec_huffman_lookup(16#5c, 16#1) -> {more, 16#3c, 16#09}; -dec_huffman_lookup(16#5c, 16#2) -> {more, 16#3c, 16#17}; -dec_huffman_lookup(16#5c, 16#3) -> {ok, 16#3c, 16#28}; -dec_huffman_lookup(16#5c, 16#4) -> {more, 16#60, 16#02}; -dec_huffman_lookup(16#5c, 16#5) -> {more, 16#60, 16#09}; -dec_huffman_lookup(16#5c, 16#6) -> {more, 16#60, 16#17}; -dec_huffman_lookup(16#5c, 16#7) -> {ok, 16#60, 16#28}; -dec_huffman_lookup(16#5c, 16#8) -> {more, 16#7b, 16#02}; -dec_huffman_lookup(16#5c, 16#9) -> {more, 16#7b, 16#09}; -dec_huffman_lookup(16#5c, 16#a) -> {more, 16#7b, 16#17}; -dec_huffman_lookup(16#5c, 16#b) -> {ok, 16#7b, 16#28}; -dec_huffman_lookup(16#5c, 16#c) -> {more, undefined, 16#61}; -dec_huffman_lookup(16#5c, 16#d) -> {more, undefined, 16#65}; -dec_huffman_lookup(16#5c, 16#e) -> {more, undefined, 16#6f}; -dec_huffman_lookup(16#5c, 16#f) -> {ok, undefined, 16#85}; -dec_huffman_lookup(16#5d, 16#0) -> {more, 16#3c, 16#03}; -dec_huffman_lookup(16#5d, 16#1) -> {more, 16#3c, 16#06}; -dec_huffman_lookup(16#5d, 16#2) -> {more, 16#3c, 16#0a}; -dec_huffman_lookup(16#5d, 16#3) -> {more, 16#3c, 16#0f}; -dec_huffman_lookup(16#5d, 16#4) -> {more, 16#3c, 16#18}; -dec_huffman_lookup(16#5d, 16#5) -> {more, 16#3c, 16#1f}; -dec_huffman_lookup(16#5d, 16#6) -> {more, 16#3c, 16#29}; -dec_huffman_lookup(16#5d, 16#7) -> {ok, 16#3c, 16#38}; -dec_huffman_lookup(16#5d, 16#8) -> {more, 16#60, 16#03}; -dec_huffman_lookup(16#5d, 16#9) -> {more, 16#60, 16#06}; -dec_huffman_lookup(16#5d, 16#a) -> {more, 16#60, 16#0a}; -dec_huffman_lookup(16#5d, 16#b) -> {more, 16#60, 16#0f}; -dec_huffman_lookup(16#5d, 16#c) -> {more, 16#60, 16#18}; -dec_huffman_lookup(16#5d, 16#d) -> {more, 16#60, 16#1f}; -dec_huffman_lookup(16#5d, 16#e) -> {more, 16#60, 16#29}; -dec_huffman_lookup(16#5d, 16#f) -> {ok, 16#60, 16#38}; -dec_huffman_lookup(16#5e, 16#0) -> {more, 16#7b, 16#03}; -dec_huffman_lookup(16#5e, 16#1) -> {more, 16#7b, 16#06}; -dec_huffman_lookup(16#5e, 16#2) -> {more, 16#7b, 16#0a}; -dec_huffman_lookup(16#5e, 16#3) -> {more, 16#7b, 16#0f}; -dec_huffman_lookup(16#5e, 16#4) -> {more, 16#7b, 16#18}; -dec_huffman_lookup(16#5e, 16#5) -> {more, 16#7b, 16#1f}; -dec_huffman_lookup(16#5e, 16#6) -> {more, 16#7b, 16#29}; -dec_huffman_lookup(16#5e, 16#7) -> {ok, 16#7b, 16#38}; -dec_huffman_lookup(16#5e, 16#8) -> {more, undefined, 16#62}; -dec_huffman_lookup(16#5e, 16#9) -> {more, undefined, 16#63}; -dec_huffman_lookup(16#5e, 16#a) -> {more, undefined, 16#66}; -dec_huffman_lookup(16#5e, 16#b) -> {more, undefined, 16#69}; -dec_huffman_lookup(16#5e, 16#c) -> {more, undefined, 16#70}; -dec_huffman_lookup(16#5e, 16#d) -> {more, undefined, 16#77}; -dec_huffman_lookup(16#5e, 16#e) -> {more, undefined, 16#86}; -dec_huffman_lookup(16#5e, 16#f) -> {ok, undefined, 16#99}; -dec_huffman_lookup(16#5f, 16#0) -> {ok, 16#5c, 16#00}; -dec_huffman_lookup(16#5f, 16#1) -> {ok, 16#c3, 16#00}; -dec_huffman_lookup(16#5f, 16#2) -> {ok, 16#d0, 16#00}; -dec_huffman_lookup(16#5f, 16#3) -> {more, undefined, 16#64}; -dec_huffman_lookup(16#5f, 16#4) -> {more, undefined, 16#67}; -dec_huffman_lookup(16#5f, 16#5) -> {more, undefined, 16#68}; -dec_huffman_lookup(16#5f, 16#6) -> {more, undefined, 16#6a}; -dec_huffman_lookup(16#5f, 16#7) -> {more, undefined, 16#6b}; -dec_huffman_lookup(16#5f, 16#8) -> {more, undefined, 16#71}; -dec_huffman_lookup(16#5f, 16#9) -> {more, undefined, 16#74}; -dec_huffman_lookup(16#5f, 16#a) -> {more, undefined, 16#78}; -dec_huffman_lookup(16#5f, 16#b) -> {more, undefined, 16#7e}; -dec_huffman_lookup(16#5f, 16#c) -> {more, undefined, 16#87}; -dec_huffman_lookup(16#5f, 16#d) -> {more, undefined, 16#8e}; -dec_huffman_lookup(16#5f, 16#e) -> {more, undefined, 16#9a}; -dec_huffman_lookup(16#5f, 16#f) -> {ok, undefined, 16#a9}; -dec_huffman_lookup(16#60, 16#0) -> {more, 16#5c, 16#01}; -dec_huffman_lookup(16#60, 16#1) -> {ok, 16#5c, 16#16}; -dec_huffman_lookup(16#60, 16#2) -> {more, 16#c3, 16#01}; -dec_huffman_lookup(16#60, 16#3) -> {ok, 16#c3, 16#16}; -dec_huffman_lookup(16#60, 16#4) -> {more, 16#d0, 16#01}; -dec_huffman_lookup(16#60, 16#5) -> {ok, 16#d0, 16#16}; -dec_huffman_lookup(16#60, 16#6) -> {ok, 16#80, 16#00}; -dec_huffman_lookup(16#60, 16#7) -> {ok, 16#82, 16#00}; -dec_huffman_lookup(16#60, 16#8) -> {ok, 16#83, 16#00}; -dec_huffman_lookup(16#60, 16#9) -> {ok, 16#a2, 16#00}; -dec_huffman_lookup(16#60, 16#a) -> {ok, 16#b8, 16#00}; -dec_huffman_lookup(16#60, 16#b) -> {ok, 16#c2, 16#00}; -dec_huffman_lookup(16#60, 16#c) -> {ok, 16#e0, 16#00}; -dec_huffman_lookup(16#60, 16#d) -> {ok, 16#e2, 16#00}; -dec_huffman_lookup(16#60, 16#e) -> {more, undefined, 16#6c}; -dec_huffman_lookup(16#60, 16#f) -> {more, undefined, 16#6d}; -dec_huffman_lookup(16#61, 16#0) -> {more, 16#5c, 16#02}; -dec_huffman_lookup(16#61, 16#1) -> {more, 16#5c, 16#09}; -dec_huffman_lookup(16#61, 16#2) -> {more, 16#5c, 16#17}; -dec_huffman_lookup(16#61, 16#3) -> {ok, 16#5c, 16#28}; -dec_huffman_lookup(16#61, 16#4) -> {more, 16#c3, 16#02}; -dec_huffman_lookup(16#61, 16#5) -> {more, 16#c3, 16#09}; -dec_huffman_lookup(16#61, 16#6) -> {more, 16#c3, 16#17}; -dec_huffman_lookup(16#61, 16#7) -> {ok, 16#c3, 16#28}; -dec_huffman_lookup(16#61, 16#8) -> {more, 16#d0, 16#02}; -dec_huffman_lookup(16#61, 16#9) -> {more, 16#d0, 16#09}; -dec_huffman_lookup(16#61, 16#a) -> {more, 16#d0, 16#17}; -dec_huffman_lookup(16#61, 16#b) -> {ok, 16#d0, 16#28}; -dec_huffman_lookup(16#61, 16#c) -> {more, 16#80, 16#01}; -dec_huffman_lookup(16#61, 16#d) -> {ok, 16#80, 16#16}; -dec_huffman_lookup(16#61, 16#e) -> {more, 16#82, 16#01}; -dec_huffman_lookup(16#61, 16#f) -> {ok, 16#82, 16#16}; -dec_huffman_lookup(16#62, 16#0) -> {more, 16#5c, 16#03}; -dec_huffman_lookup(16#62, 16#1) -> {more, 16#5c, 16#06}; -dec_huffman_lookup(16#62, 16#2) -> {more, 16#5c, 16#0a}; -dec_huffman_lookup(16#62, 16#3) -> {more, 16#5c, 16#0f}; -dec_huffman_lookup(16#62, 16#4) -> {more, 16#5c, 16#18}; -dec_huffman_lookup(16#62, 16#5) -> {more, 16#5c, 16#1f}; -dec_huffman_lookup(16#62, 16#6) -> {more, 16#5c, 16#29}; -dec_huffman_lookup(16#62, 16#7) -> {ok, 16#5c, 16#38}; -dec_huffman_lookup(16#62, 16#8) -> {more, 16#c3, 16#03}; -dec_huffman_lookup(16#62, 16#9) -> {more, 16#c3, 16#06}; -dec_huffman_lookup(16#62, 16#a) -> {more, 16#c3, 16#0a}; -dec_huffman_lookup(16#62, 16#b) -> {more, 16#c3, 16#0f}; -dec_huffman_lookup(16#62, 16#c) -> {more, 16#c3, 16#18}; -dec_huffman_lookup(16#62, 16#d) -> {more, 16#c3, 16#1f}; -dec_huffman_lookup(16#62, 16#e) -> {more, 16#c3, 16#29}; -dec_huffman_lookup(16#62, 16#f) -> {ok, 16#c3, 16#38}; -dec_huffman_lookup(16#63, 16#0) -> {more, 16#d0, 16#03}; -dec_huffman_lookup(16#63, 16#1) -> {more, 16#d0, 16#06}; -dec_huffman_lookup(16#63, 16#2) -> {more, 16#d0, 16#0a}; -dec_huffman_lookup(16#63, 16#3) -> {more, 16#d0, 16#0f}; -dec_huffman_lookup(16#63, 16#4) -> {more, 16#d0, 16#18}; -dec_huffman_lookup(16#63, 16#5) -> {more, 16#d0, 16#1f}; -dec_huffman_lookup(16#63, 16#6) -> {more, 16#d0, 16#29}; -dec_huffman_lookup(16#63, 16#7) -> {ok, 16#d0, 16#38}; -dec_huffman_lookup(16#63, 16#8) -> {more, 16#80, 16#02}; -dec_huffman_lookup(16#63, 16#9) -> {more, 16#80, 16#09}; -dec_huffman_lookup(16#63, 16#a) -> {more, 16#80, 16#17}; -dec_huffman_lookup(16#63, 16#b) -> {ok, 16#80, 16#28}; -dec_huffman_lookup(16#63, 16#c) -> {more, 16#82, 16#02}; -dec_huffman_lookup(16#63, 16#d) -> {more, 16#82, 16#09}; -dec_huffman_lookup(16#63, 16#e) -> {more, 16#82, 16#17}; -dec_huffman_lookup(16#63, 16#f) -> {ok, 16#82, 16#28}; -dec_huffman_lookup(16#64, 16#0) -> {more, 16#80, 16#03}; -dec_huffman_lookup(16#64, 16#1) -> {more, 16#80, 16#06}; -dec_huffman_lookup(16#64, 16#2) -> {more, 16#80, 16#0a}; -dec_huffman_lookup(16#64, 16#3) -> {more, 16#80, 16#0f}; -dec_huffman_lookup(16#64, 16#4) -> {more, 16#80, 16#18}; -dec_huffman_lookup(16#64, 16#5) -> {more, 16#80, 16#1f}; -dec_huffman_lookup(16#64, 16#6) -> {more, 16#80, 16#29}; -dec_huffman_lookup(16#64, 16#7) -> {ok, 16#80, 16#38}; -dec_huffman_lookup(16#64, 16#8) -> {more, 16#82, 16#03}; -dec_huffman_lookup(16#64, 16#9) -> {more, 16#82, 16#06}; -dec_huffman_lookup(16#64, 16#a) -> {more, 16#82, 16#0a}; -dec_huffman_lookup(16#64, 16#b) -> {more, 16#82, 16#0f}; -dec_huffman_lookup(16#64, 16#c) -> {more, 16#82, 16#18}; -dec_huffman_lookup(16#64, 16#d) -> {more, 16#82, 16#1f}; -dec_huffman_lookup(16#64, 16#e) -> {more, 16#82, 16#29}; -dec_huffman_lookup(16#64, 16#f) -> {ok, 16#82, 16#38}; -dec_huffman_lookup(16#65, 16#0) -> {more, 16#83, 16#01}; -dec_huffman_lookup(16#65, 16#1) -> {ok, 16#83, 16#16}; -dec_huffman_lookup(16#65, 16#2) -> {more, 16#a2, 16#01}; -dec_huffman_lookup(16#65, 16#3) -> {ok, 16#a2, 16#16}; -dec_huffman_lookup(16#65, 16#4) -> {more, 16#b8, 16#01}; -dec_huffman_lookup(16#65, 16#5) -> {ok, 16#b8, 16#16}; -dec_huffman_lookup(16#65, 16#6) -> {more, 16#c2, 16#01}; -dec_huffman_lookup(16#65, 16#7) -> {ok, 16#c2, 16#16}; -dec_huffman_lookup(16#65, 16#8) -> {more, 16#e0, 16#01}; -dec_huffman_lookup(16#65, 16#9) -> {ok, 16#e0, 16#16}; -dec_huffman_lookup(16#65, 16#a) -> {more, 16#e2, 16#01}; -dec_huffman_lookup(16#65, 16#b) -> {ok, 16#e2, 16#16}; -dec_huffman_lookup(16#65, 16#c) -> {ok, 16#99, 16#00}; -dec_huffman_lookup(16#65, 16#d) -> {ok, 16#a1, 16#00}; -dec_huffman_lookup(16#65, 16#e) -> {ok, 16#a7, 16#00}; -dec_huffman_lookup(16#65, 16#f) -> {ok, 16#ac, 16#00}; -dec_huffman_lookup(16#66, 16#0) -> {more, 16#83, 16#02}; -dec_huffman_lookup(16#66, 16#1) -> {more, 16#83, 16#09}; -dec_huffman_lookup(16#66, 16#2) -> {more, 16#83, 16#17}; -dec_huffman_lookup(16#66, 16#3) -> {ok, 16#83, 16#28}; -dec_huffman_lookup(16#66, 16#4) -> {more, 16#a2, 16#02}; -dec_huffman_lookup(16#66, 16#5) -> {more, 16#a2, 16#09}; -dec_huffman_lookup(16#66, 16#6) -> {more, 16#a2, 16#17}; -dec_huffman_lookup(16#66, 16#7) -> {ok, 16#a2, 16#28}; -dec_huffman_lookup(16#66, 16#8) -> {more, 16#b8, 16#02}; -dec_huffman_lookup(16#66, 16#9) -> {more, 16#b8, 16#09}; -dec_huffman_lookup(16#66, 16#a) -> {more, 16#b8, 16#17}; -dec_huffman_lookup(16#66, 16#b) -> {ok, 16#b8, 16#28}; -dec_huffman_lookup(16#66, 16#c) -> {more, 16#c2, 16#02}; -dec_huffman_lookup(16#66, 16#d) -> {more, 16#c2, 16#09}; -dec_huffman_lookup(16#66, 16#e) -> {more, 16#c2, 16#17}; -dec_huffman_lookup(16#66, 16#f) -> {ok, 16#c2, 16#28}; -dec_huffman_lookup(16#67, 16#0) -> {more, 16#83, 16#03}; -dec_huffman_lookup(16#67, 16#1) -> {more, 16#83, 16#06}; -dec_huffman_lookup(16#67, 16#2) -> {more, 16#83, 16#0a}; -dec_huffman_lookup(16#67, 16#3) -> {more, 16#83, 16#0f}; -dec_huffman_lookup(16#67, 16#4) -> {more, 16#83, 16#18}; -dec_huffman_lookup(16#67, 16#5) -> {more, 16#83, 16#1f}; -dec_huffman_lookup(16#67, 16#6) -> {more, 16#83, 16#29}; -dec_huffman_lookup(16#67, 16#7) -> {ok, 16#83, 16#38}; -dec_huffman_lookup(16#67, 16#8) -> {more, 16#a2, 16#03}; -dec_huffman_lookup(16#67, 16#9) -> {more, 16#a2, 16#06}; -dec_huffman_lookup(16#67, 16#a) -> {more, 16#a2, 16#0a}; -dec_huffman_lookup(16#67, 16#b) -> {more, 16#a2, 16#0f}; -dec_huffman_lookup(16#67, 16#c) -> {more, 16#a2, 16#18}; -dec_huffman_lookup(16#67, 16#d) -> {more, 16#a2, 16#1f}; -dec_huffman_lookup(16#67, 16#e) -> {more, 16#a2, 16#29}; -dec_huffman_lookup(16#67, 16#f) -> {ok, 16#a2, 16#38}; -dec_huffman_lookup(16#68, 16#0) -> {more, 16#b8, 16#03}; -dec_huffman_lookup(16#68, 16#1) -> {more, 16#b8, 16#06}; -dec_huffman_lookup(16#68, 16#2) -> {more, 16#b8, 16#0a}; -dec_huffman_lookup(16#68, 16#3) -> {more, 16#b8, 16#0f}; -dec_huffman_lookup(16#68, 16#4) -> {more, 16#b8, 16#18}; -dec_huffman_lookup(16#68, 16#5) -> {more, 16#b8, 16#1f}; -dec_huffman_lookup(16#68, 16#6) -> {more, 16#b8, 16#29}; -dec_huffman_lookup(16#68, 16#7) -> {ok, 16#b8, 16#38}; -dec_huffman_lookup(16#68, 16#8) -> {more, 16#c2, 16#03}; -dec_huffman_lookup(16#68, 16#9) -> {more, 16#c2, 16#06}; -dec_huffman_lookup(16#68, 16#a) -> {more, 16#c2, 16#0a}; -dec_huffman_lookup(16#68, 16#b) -> {more, 16#c2, 16#0f}; -dec_huffman_lookup(16#68, 16#c) -> {more, 16#c2, 16#18}; -dec_huffman_lookup(16#68, 16#d) -> {more, 16#c2, 16#1f}; -dec_huffman_lookup(16#68, 16#e) -> {more, 16#c2, 16#29}; -dec_huffman_lookup(16#68, 16#f) -> {ok, 16#c2, 16#38}; -dec_huffman_lookup(16#69, 16#0) -> {more, 16#e0, 16#02}; -dec_huffman_lookup(16#69, 16#1) -> {more, 16#e0, 16#09}; -dec_huffman_lookup(16#69, 16#2) -> {more, 16#e0, 16#17}; -dec_huffman_lookup(16#69, 16#3) -> {ok, 16#e0, 16#28}; -dec_huffman_lookup(16#69, 16#4) -> {more, 16#e2, 16#02}; -dec_huffman_lookup(16#69, 16#5) -> {more, 16#e2, 16#09}; -dec_huffman_lookup(16#69, 16#6) -> {more, 16#e2, 16#17}; -dec_huffman_lookup(16#69, 16#7) -> {ok, 16#e2, 16#28}; -dec_huffman_lookup(16#69, 16#8) -> {more, 16#99, 16#01}; -dec_huffman_lookup(16#69, 16#9) -> {ok, 16#99, 16#16}; -dec_huffman_lookup(16#69, 16#a) -> {more, 16#a1, 16#01}; -dec_huffman_lookup(16#69, 16#b) -> {ok, 16#a1, 16#16}; -dec_huffman_lookup(16#69, 16#c) -> {more, 16#a7, 16#01}; -dec_huffman_lookup(16#69, 16#d) -> {ok, 16#a7, 16#16}; -dec_huffman_lookup(16#69, 16#e) -> {more, 16#ac, 16#01}; -dec_huffman_lookup(16#69, 16#f) -> {ok, 16#ac, 16#16}; -dec_huffman_lookup(16#6a, 16#0) -> {more, 16#e0, 16#03}; -dec_huffman_lookup(16#6a, 16#1) -> {more, 16#e0, 16#06}; -dec_huffman_lookup(16#6a, 16#2) -> {more, 16#e0, 16#0a}; -dec_huffman_lookup(16#6a, 16#3) -> {more, 16#e0, 16#0f}; -dec_huffman_lookup(16#6a, 16#4) -> {more, 16#e0, 16#18}; -dec_huffman_lookup(16#6a, 16#5) -> {more, 16#e0, 16#1f}; -dec_huffman_lookup(16#6a, 16#6) -> {more, 16#e0, 16#29}; -dec_huffman_lookup(16#6a, 16#7) -> {ok, 16#e0, 16#38}; -dec_huffman_lookup(16#6a, 16#8) -> {more, 16#e2, 16#03}; -dec_huffman_lookup(16#6a, 16#9) -> {more, 16#e2, 16#06}; -dec_huffman_lookup(16#6a, 16#a) -> {more, 16#e2, 16#0a}; -dec_huffman_lookup(16#6a, 16#b) -> {more, 16#e2, 16#0f}; -dec_huffman_lookup(16#6a, 16#c) -> {more, 16#e2, 16#18}; -dec_huffman_lookup(16#6a, 16#d) -> {more, 16#e2, 16#1f}; -dec_huffman_lookup(16#6a, 16#e) -> {more, 16#e2, 16#29}; -dec_huffman_lookup(16#6a, 16#f) -> {ok, 16#e2, 16#38}; -dec_huffman_lookup(16#6b, 16#0) -> {more, 16#99, 16#02}; -dec_huffman_lookup(16#6b, 16#1) -> {more, 16#99, 16#09}; -dec_huffman_lookup(16#6b, 16#2) -> {more, 16#99, 16#17}; -dec_huffman_lookup(16#6b, 16#3) -> {ok, 16#99, 16#28}; -dec_huffman_lookup(16#6b, 16#4) -> {more, 16#a1, 16#02}; -dec_huffman_lookup(16#6b, 16#5) -> {more, 16#a1, 16#09}; -dec_huffman_lookup(16#6b, 16#6) -> {more, 16#a1, 16#17}; -dec_huffman_lookup(16#6b, 16#7) -> {ok, 16#a1, 16#28}; -dec_huffman_lookup(16#6b, 16#8) -> {more, 16#a7, 16#02}; -dec_huffman_lookup(16#6b, 16#9) -> {more, 16#a7, 16#09}; -dec_huffman_lookup(16#6b, 16#a) -> {more, 16#a7, 16#17}; -dec_huffman_lookup(16#6b, 16#b) -> {ok, 16#a7, 16#28}; -dec_huffman_lookup(16#6b, 16#c) -> {more, 16#ac, 16#02}; -dec_huffman_lookup(16#6b, 16#d) -> {more, 16#ac, 16#09}; -dec_huffman_lookup(16#6b, 16#e) -> {more, 16#ac, 16#17}; -dec_huffman_lookup(16#6b, 16#f) -> {ok, 16#ac, 16#28}; -dec_huffman_lookup(16#6c, 16#0) -> {more, 16#99, 16#03}; -dec_huffman_lookup(16#6c, 16#1) -> {more, 16#99, 16#06}; -dec_huffman_lookup(16#6c, 16#2) -> {more, 16#99, 16#0a}; -dec_huffman_lookup(16#6c, 16#3) -> {more, 16#99, 16#0f}; -dec_huffman_lookup(16#6c, 16#4) -> {more, 16#99, 16#18}; -dec_huffman_lookup(16#6c, 16#5) -> {more, 16#99, 16#1f}; -dec_huffman_lookup(16#6c, 16#6) -> {more, 16#99, 16#29}; -dec_huffman_lookup(16#6c, 16#7) -> {ok, 16#99, 16#38}; -dec_huffman_lookup(16#6c, 16#8) -> {more, 16#a1, 16#03}; -dec_huffman_lookup(16#6c, 16#9) -> {more, 16#a1, 16#06}; -dec_huffman_lookup(16#6c, 16#a) -> {more, 16#a1, 16#0a}; -dec_huffman_lookup(16#6c, 16#b) -> {more, 16#a1, 16#0f}; -dec_huffman_lookup(16#6c, 16#c) -> {more, 16#a1, 16#18}; -dec_huffman_lookup(16#6c, 16#d) -> {more, 16#a1, 16#1f}; -dec_huffman_lookup(16#6c, 16#e) -> {more, 16#a1, 16#29}; -dec_huffman_lookup(16#6c, 16#f) -> {ok, 16#a1, 16#38}; -dec_huffman_lookup(16#6d, 16#0) -> {more, 16#a7, 16#03}; -dec_huffman_lookup(16#6d, 16#1) -> {more, 16#a7, 16#06}; -dec_huffman_lookup(16#6d, 16#2) -> {more, 16#a7, 16#0a}; -dec_huffman_lookup(16#6d, 16#3) -> {more, 16#a7, 16#0f}; -dec_huffman_lookup(16#6d, 16#4) -> {more, 16#a7, 16#18}; -dec_huffman_lookup(16#6d, 16#5) -> {more, 16#a7, 16#1f}; -dec_huffman_lookup(16#6d, 16#6) -> {more, 16#a7, 16#29}; -dec_huffman_lookup(16#6d, 16#7) -> {ok, 16#a7, 16#38}; -dec_huffman_lookup(16#6d, 16#8) -> {more, 16#ac, 16#03}; -dec_huffman_lookup(16#6d, 16#9) -> {more, 16#ac, 16#06}; -dec_huffman_lookup(16#6d, 16#a) -> {more, 16#ac, 16#0a}; -dec_huffman_lookup(16#6d, 16#b) -> {more, 16#ac, 16#0f}; -dec_huffman_lookup(16#6d, 16#c) -> {more, 16#ac, 16#18}; -dec_huffman_lookup(16#6d, 16#d) -> {more, 16#ac, 16#1f}; -dec_huffman_lookup(16#6d, 16#e) -> {more, 16#ac, 16#29}; -dec_huffman_lookup(16#6d, 16#f) -> {ok, 16#ac, 16#38}; -dec_huffman_lookup(16#6e, 16#0) -> {more, undefined, 16#72}; -dec_huffman_lookup(16#6e, 16#1) -> {more, undefined, 16#73}; -dec_huffman_lookup(16#6e, 16#2) -> {more, undefined, 16#75}; -dec_huffman_lookup(16#6e, 16#3) -> {more, undefined, 16#76}; -dec_huffman_lookup(16#6e, 16#4) -> {more, undefined, 16#79}; -dec_huffman_lookup(16#6e, 16#5) -> {more, undefined, 16#7b}; -dec_huffman_lookup(16#6e, 16#6) -> {more, undefined, 16#7f}; -dec_huffman_lookup(16#6e, 16#7) -> {more, undefined, 16#82}; -dec_huffman_lookup(16#6e, 16#8) -> {more, undefined, 16#88}; -dec_huffman_lookup(16#6e, 16#9) -> {more, undefined, 16#8b}; -dec_huffman_lookup(16#6e, 16#a) -> {more, undefined, 16#8f}; -dec_huffman_lookup(16#6e, 16#b) -> {more, undefined, 16#92}; -dec_huffman_lookup(16#6e, 16#c) -> {more, undefined, 16#9b}; -dec_huffman_lookup(16#6e, 16#d) -> {more, undefined, 16#a2}; -dec_huffman_lookup(16#6e, 16#e) -> {more, undefined, 16#aa}; -dec_huffman_lookup(16#6e, 16#f) -> {ok, undefined, 16#b4}; -dec_huffman_lookup(16#6f, 16#0) -> {ok, 16#b0, 16#00}; -dec_huffman_lookup(16#6f, 16#1) -> {ok, 16#b1, 16#00}; -dec_huffman_lookup(16#6f, 16#2) -> {ok, 16#b3, 16#00}; -dec_huffman_lookup(16#6f, 16#3) -> {ok, 16#d1, 16#00}; -dec_huffman_lookup(16#6f, 16#4) -> {ok, 16#d8, 16#00}; -dec_huffman_lookup(16#6f, 16#5) -> {ok, 16#d9, 16#00}; -dec_huffman_lookup(16#6f, 16#6) -> {ok, 16#e3, 16#00}; -dec_huffman_lookup(16#6f, 16#7) -> {ok, 16#e5, 16#00}; -dec_huffman_lookup(16#6f, 16#8) -> {ok, 16#e6, 16#00}; -dec_huffman_lookup(16#6f, 16#9) -> {more, undefined, 16#7a}; -dec_huffman_lookup(16#6f, 16#a) -> {more, undefined, 16#7c}; -dec_huffman_lookup(16#6f, 16#b) -> {more, undefined, 16#7d}; -dec_huffman_lookup(16#6f, 16#c) -> {more, undefined, 16#80}; -dec_huffman_lookup(16#6f, 16#d) -> {more, undefined, 16#81}; -dec_huffman_lookup(16#6f, 16#e) -> {more, undefined, 16#83}; -dec_huffman_lookup(16#6f, 16#f) -> {more, undefined, 16#84}; -dec_huffman_lookup(16#70, 16#0) -> {more, 16#b0, 16#01}; -dec_huffman_lookup(16#70, 16#1) -> {ok, 16#b0, 16#16}; -dec_huffman_lookup(16#70, 16#2) -> {more, 16#b1, 16#01}; -dec_huffman_lookup(16#70, 16#3) -> {ok, 16#b1, 16#16}; -dec_huffman_lookup(16#70, 16#4) -> {more, 16#b3, 16#01}; -dec_huffman_lookup(16#70, 16#5) -> {ok, 16#b3, 16#16}; -dec_huffman_lookup(16#70, 16#6) -> {more, 16#d1, 16#01}; -dec_huffman_lookup(16#70, 16#7) -> {ok, 16#d1, 16#16}; -dec_huffman_lookup(16#70, 16#8) -> {more, 16#d8, 16#01}; -dec_huffman_lookup(16#70, 16#9) -> {ok, 16#d8, 16#16}; -dec_huffman_lookup(16#70, 16#a) -> {more, 16#d9, 16#01}; -dec_huffman_lookup(16#70, 16#b) -> {ok, 16#d9, 16#16}; -dec_huffman_lookup(16#70, 16#c) -> {more, 16#e3, 16#01}; -dec_huffman_lookup(16#70, 16#d) -> {ok, 16#e3, 16#16}; -dec_huffman_lookup(16#70, 16#e) -> {more, 16#e5, 16#01}; -dec_huffman_lookup(16#70, 16#f) -> {ok, 16#e5, 16#16}; -dec_huffman_lookup(16#71, 16#0) -> {more, 16#b0, 16#02}; -dec_huffman_lookup(16#71, 16#1) -> {more, 16#b0, 16#09}; -dec_huffman_lookup(16#71, 16#2) -> {more, 16#b0, 16#17}; -dec_huffman_lookup(16#71, 16#3) -> {ok, 16#b0, 16#28}; -dec_huffman_lookup(16#71, 16#4) -> {more, 16#b1, 16#02}; -dec_huffman_lookup(16#71, 16#5) -> {more, 16#b1, 16#09}; -dec_huffman_lookup(16#71, 16#6) -> {more, 16#b1, 16#17}; -dec_huffman_lookup(16#71, 16#7) -> {ok, 16#b1, 16#28}; -dec_huffman_lookup(16#71, 16#8) -> {more, 16#b3, 16#02}; -dec_huffman_lookup(16#71, 16#9) -> {more, 16#b3, 16#09}; -dec_huffman_lookup(16#71, 16#a) -> {more, 16#b3, 16#17}; -dec_huffman_lookup(16#71, 16#b) -> {ok, 16#b3, 16#28}; -dec_huffman_lookup(16#71, 16#c) -> {more, 16#d1, 16#02}; -dec_huffman_lookup(16#71, 16#d) -> {more, 16#d1, 16#09}; -dec_huffman_lookup(16#71, 16#e) -> {more, 16#d1, 16#17}; -dec_huffman_lookup(16#71, 16#f) -> {ok, 16#d1, 16#28}; -dec_huffman_lookup(16#72, 16#0) -> {more, 16#b0, 16#03}; -dec_huffman_lookup(16#72, 16#1) -> {more, 16#b0, 16#06}; -dec_huffman_lookup(16#72, 16#2) -> {more, 16#b0, 16#0a}; -dec_huffman_lookup(16#72, 16#3) -> {more, 16#b0, 16#0f}; -dec_huffman_lookup(16#72, 16#4) -> {more, 16#b0, 16#18}; -dec_huffman_lookup(16#72, 16#5) -> {more, 16#b0, 16#1f}; -dec_huffman_lookup(16#72, 16#6) -> {more, 16#b0, 16#29}; -dec_huffman_lookup(16#72, 16#7) -> {ok, 16#b0, 16#38}; -dec_huffman_lookup(16#72, 16#8) -> {more, 16#b1, 16#03}; -dec_huffman_lookup(16#72, 16#9) -> {more, 16#b1, 16#06}; -dec_huffman_lookup(16#72, 16#a) -> {more, 16#b1, 16#0a}; -dec_huffman_lookup(16#72, 16#b) -> {more, 16#b1, 16#0f}; -dec_huffman_lookup(16#72, 16#c) -> {more, 16#b1, 16#18}; -dec_huffman_lookup(16#72, 16#d) -> {more, 16#b1, 16#1f}; -dec_huffman_lookup(16#72, 16#e) -> {more, 16#b1, 16#29}; -dec_huffman_lookup(16#72, 16#f) -> {ok, 16#b1, 16#38}; -dec_huffman_lookup(16#73, 16#0) -> {more, 16#b3, 16#03}; -dec_huffman_lookup(16#73, 16#1) -> {more, 16#b3, 16#06}; -dec_huffman_lookup(16#73, 16#2) -> {more, 16#b3, 16#0a}; -dec_huffman_lookup(16#73, 16#3) -> {more, 16#b3, 16#0f}; -dec_huffman_lookup(16#73, 16#4) -> {more, 16#b3, 16#18}; -dec_huffman_lookup(16#73, 16#5) -> {more, 16#b3, 16#1f}; -dec_huffman_lookup(16#73, 16#6) -> {more, 16#b3, 16#29}; -dec_huffman_lookup(16#73, 16#7) -> {ok, 16#b3, 16#38}; -dec_huffman_lookup(16#73, 16#8) -> {more, 16#d1, 16#03}; -dec_huffman_lookup(16#73, 16#9) -> {more, 16#d1, 16#06}; -dec_huffman_lookup(16#73, 16#a) -> {more, 16#d1, 16#0a}; -dec_huffman_lookup(16#73, 16#b) -> {more, 16#d1, 16#0f}; -dec_huffman_lookup(16#73, 16#c) -> {more, 16#d1, 16#18}; -dec_huffman_lookup(16#73, 16#d) -> {more, 16#d1, 16#1f}; -dec_huffman_lookup(16#73, 16#e) -> {more, 16#d1, 16#29}; -dec_huffman_lookup(16#73, 16#f) -> {ok, 16#d1, 16#38}; -dec_huffman_lookup(16#74, 16#0) -> {more, 16#d8, 16#02}; -dec_huffman_lookup(16#74, 16#1) -> {more, 16#d8, 16#09}; -dec_huffman_lookup(16#74, 16#2) -> {more, 16#d8, 16#17}; -dec_huffman_lookup(16#74, 16#3) -> {ok, 16#d8, 16#28}; -dec_huffman_lookup(16#74, 16#4) -> {more, 16#d9, 16#02}; -dec_huffman_lookup(16#74, 16#5) -> {more, 16#d9, 16#09}; -dec_huffman_lookup(16#74, 16#6) -> {more, 16#d9, 16#17}; -dec_huffman_lookup(16#74, 16#7) -> {ok, 16#d9, 16#28}; -dec_huffman_lookup(16#74, 16#8) -> {more, 16#e3, 16#02}; -dec_huffman_lookup(16#74, 16#9) -> {more, 16#e3, 16#09}; -dec_huffman_lookup(16#74, 16#a) -> {more, 16#e3, 16#17}; -dec_huffman_lookup(16#74, 16#b) -> {ok, 16#e3, 16#28}; -dec_huffman_lookup(16#74, 16#c) -> {more, 16#e5, 16#02}; -dec_huffman_lookup(16#74, 16#d) -> {more, 16#e5, 16#09}; -dec_huffman_lookup(16#74, 16#e) -> {more, 16#e5, 16#17}; -dec_huffman_lookup(16#74, 16#f) -> {ok, 16#e5, 16#28}; -dec_huffman_lookup(16#75, 16#0) -> {more, 16#d8, 16#03}; -dec_huffman_lookup(16#75, 16#1) -> {more, 16#d8, 16#06}; -dec_huffman_lookup(16#75, 16#2) -> {more, 16#d8, 16#0a}; -dec_huffman_lookup(16#75, 16#3) -> {more, 16#d8, 16#0f}; -dec_huffman_lookup(16#75, 16#4) -> {more, 16#d8, 16#18}; -dec_huffman_lookup(16#75, 16#5) -> {more, 16#d8, 16#1f}; -dec_huffman_lookup(16#75, 16#6) -> {more, 16#d8, 16#29}; -dec_huffman_lookup(16#75, 16#7) -> {ok, 16#d8, 16#38}; -dec_huffman_lookup(16#75, 16#8) -> {more, 16#d9, 16#03}; -dec_huffman_lookup(16#75, 16#9) -> {more, 16#d9, 16#06}; -dec_huffman_lookup(16#75, 16#a) -> {more, 16#d9, 16#0a}; -dec_huffman_lookup(16#75, 16#b) -> {more, 16#d9, 16#0f}; -dec_huffman_lookup(16#75, 16#c) -> {more, 16#d9, 16#18}; -dec_huffman_lookup(16#75, 16#d) -> {more, 16#d9, 16#1f}; -dec_huffman_lookup(16#75, 16#e) -> {more, 16#d9, 16#29}; -dec_huffman_lookup(16#75, 16#f) -> {ok, 16#d9, 16#38}; -dec_huffman_lookup(16#76, 16#0) -> {more, 16#e3, 16#03}; -dec_huffman_lookup(16#76, 16#1) -> {more, 16#e3, 16#06}; -dec_huffman_lookup(16#76, 16#2) -> {more, 16#e3, 16#0a}; -dec_huffman_lookup(16#76, 16#3) -> {more, 16#e3, 16#0f}; -dec_huffman_lookup(16#76, 16#4) -> {more, 16#e3, 16#18}; -dec_huffman_lookup(16#76, 16#5) -> {more, 16#e3, 16#1f}; -dec_huffman_lookup(16#76, 16#6) -> {more, 16#e3, 16#29}; -dec_huffman_lookup(16#76, 16#7) -> {ok, 16#e3, 16#38}; -dec_huffman_lookup(16#76, 16#8) -> {more, 16#e5, 16#03}; -dec_huffman_lookup(16#76, 16#9) -> {more, 16#e5, 16#06}; -dec_huffman_lookup(16#76, 16#a) -> {more, 16#e5, 16#0a}; -dec_huffman_lookup(16#76, 16#b) -> {more, 16#e5, 16#0f}; -dec_huffman_lookup(16#76, 16#c) -> {more, 16#e5, 16#18}; -dec_huffman_lookup(16#76, 16#d) -> {more, 16#e5, 16#1f}; -dec_huffman_lookup(16#76, 16#e) -> {more, 16#e5, 16#29}; -dec_huffman_lookup(16#76, 16#f) -> {ok, 16#e5, 16#38}; -dec_huffman_lookup(16#77, 16#0) -> {more, 16#e6, 16#01}; -dec_huffman_lookup(16#77, 16#1) -> {ok, 16#e6, 16#16}; -dec_huffman_lookup(16#77, 16#2) -> {ok, 16#81, 16#00}; -dec_huffman_lookup(16#77, 16#3) -> {ok, 16#84, 16#00}; -dec_huffman_lookup(16#77, 16#4) -> {ok, 16#85, 16#00}; -dec_huffman_lookup(16#77, 16#5) -> {ok, 16#86, 16#00}; -dec_huffman_lookup(16#77, 16#6) -> {ok, 16#88, 16#00}; -dec_huffman_lookup(16#77, 16#7) -> {ok, 16#92, 16#00}; -dec_huffman_lookup(16#77, 16#8) -> {ok, 16#9a, 16#00}; -dec_huffman_lookup(16#77, 16#9) -> {ok, 16#9c, 16#00}; -dec_huffman_lookup(16#77, 16#a) -> {ok, 16#a0, 16#00}; -dec_huffman_lookup(16#77, 16#b) -> {ok, 16#a3, 16#00}; -dec_huffman_lookup(16#77, 16#c) -> {ok, 16#a4, 16#00}; -dec_huffman_lookup(16#77, 16#d) -> {ok, 16#a9, 16#00}; -dec_huffman_lookup(16#77, 16#e) -> {ok, 16#aa, 16#00}; -dec_huffman_lookup(16#77, 16#f) -> {ok, 16#ad, 16#00}; -dec_huffman_lookup(16#78, 16#0) -> {more, 16#e6, 16#02}; -dec_huffman_lookup(16#78, 16#1) -> {more, 16#e6, 16#09}; -dec_huffman_lookup(16#78, 16#2) -> {more, 16#e6, 16#17}; -dec_huffman_lookup(16#78, 16#3) -> {ok, 16#e6, 16#28}; -dec_huffman_lookup(16#78, 16#4) -> {more, 16#81, 16#01}; -dec_huffman_lookup(16#78, 16#5) -> {ok, 16#81, 16#16}; -dec_huffman_lookup(16#78, 16#6) -> {more, 16#84, 16#01}; -dec_huffman_lookup(16#78, 16#7) -> {ok, 16#84, 16#16}; -dec_huffman_lookup(16#78, 16#8) -> {more, 16#85, 16#01}; -dec_huffman_lookup(16#78, 16#9) -> {ok, 16#85, 16#16}; -dec_huffman_lookup(16#78, 16#a) -> {more, 16#86, 16#01}; -dec_huffman_lookup(16#78, 16#b) -> {ok, 16#86, 16#16}; -dec_huffman_lookup(16#78, 16#c) -> {more, 16#88, 16#01}; -dec_huffman_lookup(16#78, 16#d) -> {ok, 16#88, 16#16}; -dec_huffman_lookup(16#78, 16#e) -> {more, 16#92, 16#01}; -dec_huffman_lookup(16#78, 16#f) -> {ok, 16#92, 16#16}; -dec_huffman_lookup(16#79, 16#0) -> {more, 16#e6, 16#03}; -dec_huffman_lookup(16#79, 16#1) -> {more, 16#e6, 16#06}; -dec_huffman_lookup(16#79, 16#2) -> {more, 16#e6, 16#0a}; -dec_huffman_lookup(16#79, 16#3) -> {more, 16#e6, 16#0f}; -dec_huffman_lookup(16#79, 16#4) -> {more, 16#e6, 16#18}; -dec_huffman_lookup(16#79, 16#5) -> {more, 16#e6, 16#1f}; -dec_huffman_lookup(16#79, 16#6) -> {more, 16#e6, 16#29}; -dec_huffman_lookup(16#79, 16#7) -> {ok, 16#e6, 16#38}; -dec_huffman_lookup(16#79, 16#8) -> {more, 16#81, 16#02}; -dec_huffman_lookup(16#79, 16#9) -> {more, 16#81, 16#09}; -dec_huffman_lookup(16#79, 16#a) -> {more, 16#81, 16#17}; -dec_huffman_lookup(16#79, 16#b) -> {ok, 16#81, 16#28}; -dec_huffman_lookup(16#79, 16#c) -> {more, 16#84, 16#02}; -dec_huffman_lookup(16#79, 16#d) -> {more, 16#84, 16#09}; -dec_huffman_lookup(16#79, 16#e) -> {more, 16#84, 16#17}; -dec_huffman_lookup(16#79, 16#f) -> {ok, 16#84, 16#28}; -dec_huffman_lookup(16#7a, 16#0) -> {more, 16#81, 16#03}; -dec_huffman_lookup(16#7a, 16#1) -> {more, 16#81, 16#06}; -dec_huffman_lookup(16#7a, 16#2) -> {more, 16#81, 16#0a}; -dec_huffman_lookup(16#7a, 16#3) -> {more, 16#81, 16#0f}; -dec_huffman_lookup(16#7a, 16#4) -> {more, 16#81, 16#18}; -dec_huffman_lookup(16#7a, 16#5) -> {more, 16#81, 16#1f}; -dec_huffman_lookup(16#7a, 16#6) -> {more, 16#81, 16#29}; -dec_huffman_lookup(16#7a, 16#7) -> {ok, 16#81, 16#38}; -dec_huffman_lookup(16#7a, 16#8) -> {more, 16#84, 16#03}; -dec_huffman_lookup(16#7a, 16#9) -> {more, 16#84, 16#06}; -dec_huffman_lookup(16#7a, 16#a) -> {more, 16#84, 16#0a}; -dec_huffman_lookup(16#7a, 16#b) -> {more, 16#84, 16#0f}; -dec_huffman_lookup(16#7a, 16#c) -> {more, 16#84, 16#18}; -dec_huffman_lookup(16#7a, 16#d) -> {more, 16#84, 16#1f}; -dec_huffman_lookup(16#7a, 16#e) -> {more, 16#84, 16#29}; -dec_huffman_lookup(16#7a, 16#f) -> {ok, 16#84, 16#38}; -dec_huffman_lookup(16#7b, 16#0) -> {more, 16#85, 16#02}; -dec_huffman_lookup(16#7b, 16#1) -> {more, 16#85, 16#09}; -dec_huffman_lookup(16#7b, 16#2) -> {more, 16#85, 16#17}; -dec_huffman_lookup(16#7b, 16#3) -> {ok, 16#85, 16#28}; -dec_huffman_lookup(16#7b, 16#4) -> {more, 16#86, 16#02}; -dec_huffman_lookup(16#7b, 16#5) -> {more, 16#86, 16#09}; -dec_huffman_lookup(16#7b, 16#6) -> {more, 16#86, 16#17}; -dec_huffman_lookup(16#7b, 16#7) -> {ok, 16#86, 16#28}; -dec_huffman_lookup(16#7b, 16#8) -> {more, 16#88, 16#02}; -dec_huffman_lookup(16#7b, 16#9) -> {more, 16#88, 16#09}; -dec_huffman_lookup(16#7b, 16#a) -> {more, 16#88, 16#17}; -dec_huffman_lookup(16#7b, 16#b) -> {ok, 16#88, 16#28}; -dec_huffman_lookup(16#7b, 16#c) -> {more, 16#92, 16#02}; -dec_huffman_lookup(16#7b, 16#d) -> {more, 16#92, 16#09}; -dec_huffman_lookup(16#7b, 16#e) -> {more, 16#92, 16#17}; -dec_huffman_lookup(16#7b, 16#f) -> {ok, 16#92, 16#28}; -dec_huffman_lookup(16#7c, 16#0) -> {more, 16#85, 16#03}; -dec_huffman_lookup(16#7c, 16#1) -> {more, 16#85, 16#06}; -dec_huffman_lookup(16#7c, 16#2) -> {more, 16#85, 16#0a}; -dec_huffman_lookup(16#7c, 16#3) -> {more, 16#85, 16#0f}; -dec_huffman_lookup(16#7c, 16#4) -> {more, 16#85, 16#18}; -dec_huffman_lookup(16#7c, 16#5) -> {more, 16#85, 16#1f}; -dec_huffman_lookup(16#7c, 16#6) -> {more, 16#85, 16#29}; -dec_huffman_lookup(16#7c, 16#7) -> {ok, 16#85, 16#38}; -dec_huffman_lookup(16#7c, 16#8) -> {more, 16#86, 16#03}; -dec_huffman_lookup(16#7c, 16#9) -> {more, 16#86, 16#06}; -dec_huffman_lookup(16#7c, 16#a) -> {more, 16#86, 16#0a}; -dec_huffman_lookup(16#7c, 16#b) -> {more, 16#86, 16#0f}; -dec_huffman_lookup(16#7c, 16#c) -> {more, 16#86, 16#18}; -dec_huffman_lookup(16#7c, 16#d) -> {more, 16#86, 16#1f}; -dec_huffman_lookup(16#7c, 16#e) -> {more, 16#86, 16#29}; -dec_huffman_lookup(16#7c, 16#f) -> {ok, 16#86, 16#38}; -dec_huffman_lookup(16#7d, 16#0) -> {more, 16#88, 16#03}; -dec_huffman_lookup(16#7d, 16#1) -> {more, 16#88, 16#06}; -dec_huffman_lookup(16#7d, 16#2) -> {more, 16#88, 16#0a}; -dec_huffman_lookup(16#7d, 16#3) -> {more, 16#88, 16#0f}; -dec_huffman_lookup(16#7d, 16#4) -> {more, 16#88, 16#18}; -dec_huffman_lookup(16#7d, 16#5) -> {more, 16#88, 16#1f}; -dec_huffman_lookup(16#7d, 16#6) -> {more, 16#88, 16#29}; -dec_huffman_lookup(16#7d, 16#7) -> {ok, 16#88, 16#38}; -dec_huffman_lookup(16#7d, 16#8) -> {more, 16#92, 16#03}; -dec_huffman_lookup(16#7d, 16#9) -> {more, 16#92, 16#06}; -dec_huffman_lookup(16#7d, 16#a) -> {more, 16#92, 16#0a}; -dec_huffman_lookup(16#7d, 16#b) -> {more, 16#92, 16#0f}; -dec_huffman_lookup(16#7d, 16#c) -> {more, 16#92, 16#18}; -dec_huffman_lookup(16#7d, 16#d) -> {more, 16#92, 16#1f}; -dec_huffman_lookup(16#7d, 16#e) -> {more, 16#92, 16#29}; -dec_huffman_lookup(16#7d, 16#f) -> {ok, 16#92, 16#38}; -dec_huffman_lookup(16#7e, 16#0) -> {more, 16#9a, 16#01}; -dec_huffman_lookup(16#7e, 16#1) -> {ok, 16#9a, 16#16}; -dec_huffman_lookup(16#7e, 16#2) -> {more, 16#9c, 16#01}; -dec_huffman_lookup(16#7e, 16#3) -> {ok, 16#9c, 16#16}; -dec_huffman_lookup(16#7e, 16#4) -> {more, 16#a0, 16#01}; -dec_huffman_lookup(16#7e, 16#5) -> {ok, 16#a0, 16#16}; -dec_huffman_lookup(16#7e, 16#6) -> {more, 16#a3, 16#01}; -dec_huffman_lookup(16#7e, 16#7) -> {ok, 16#a3, 16#16}; -dec_huffman_lookup(16#7e, 16#8) -> {more, 16#a4, 16#01}; -dec_huffman_lookup(16#7e, 16#9) -> {ok, 16#a4, 16#16}; -dec_huffman_lookup(16#7e, 16#a) -> {more, 16#a9, 16#01}; -dec_huffman_lookup(16#7e, 16#b) -> {ok, 16#a9, 16#16}; -dec_huffman_lookup(16#7e, 16#c) -> {more, 16#aa, 16#01}; -dec_huffman_lookup(16#7e, 16#d) -> {ok, 16#aa, 16#16}; -dec_huffman_lookup(16#7e, 16#e) -> {more, 16#ad, 16#01}; -dec_huffman_lookup(16#7e, 16#f) -> {ok, 16#ad, 16#16}; -dec_huffman_lookup(16#7f, 16#0) -> {more, 16#9a, 16#02}; -dec_huffman_lookup(16#7f, 16#1) -> {more, 16#9a, 16#09}; -dec_huffman_lookup(16#7f, 16#2) -> {more, 16#9a, 16#17}; -dec_huffman_lookup(16#7f, 16#3) -> {ok, 16#9a, 16#28}; -dec_huffman_lookup(16#7f, 16#4) -> {more, 16#9c, 16#02}; -dec_huffman_lookup(16#7f, 16#5) -> {more, 16#9c, 16#09}; -dec_huffman_lookup(16#7f, 16#6) -> {more, 16#9c, 16#17}; -dec_huffman_lookup(16#7f, 16#7) -> {ok, 16#9c, 16#28}; -dec_huffman_lookup(16#7f, 16#8) -> {more, 16#a0, 16#02}; -dec_huffman_lookup(16#7f, 16#9) -> {more, 16#a0, 16#09}; -dec_huffman_lookup(16#7f, 16#a) -> {more, 16#a0, 16#17}; -dec_huffman_lookup(16#7f, 16#b) -> {ok, 16#a0, 16#28}; -dec_huffman_lookup(16#7f, 16#c) -> {more, 16#a3, 16#02}; -dec_huffman_lookup(16#7f, 16#d) -> {more, 16#a3, 16#09}; -dec_huffman_lookup(16#7f, 16#e) -> {more, 16#a3, 16#17}; -dec_huffman_lookup(16#7f, 16#f) -> {ok, 16#a3, 16#28}; -dec_huffman_lookup(16#80, 16#0) -> {more, 16#9a, 16#03}; -dec_huffman_lookup(16#80, 16#1) -> {more, 16#9a, 16#06}; -dec_huffman_lookup(16#80, 16#2) -> {more, 16#9a, 16#0a}; -dec_huffman_lookup(16#80, 16#3) -> {more, 16#9a, 16#0f}; -dec_huffman_lookup(16#80, 16#4) -> {more, 16#9a, 16#18}; -dec_huffman_lookup(16#80, 16#5) -> {more, 16#9a, 16#1f}; -dec_huffman_lookup(16#80, 16#6) -> {more, 16#9a, 16#29}; -dec_huffman_lookup(16#80, 16#7) -> {ok, 16#9a, 16#38}; -dec_huffman_lookup(16#80, 16#8) -> {more, 16#9c, 16#03}; -dec_huffman_lookup(16#80, 16#9) -> {more, 16#9c, 16#06}; -dec_huffman_lookup(16#80, 16#a) -> {more, 16#9c, 16#0a}; -dec_huffman_lookup(16#80, 16#b) -> {more, 16#9c, 16#0f}; -dec_huffman_lookup(16#80, 16#c) -> {more, 16#9c, 16#18}; -dec_huffman_lookup(16#80, 16#d) -> {more, 16#9c, 16#1f}; -dec_huffman_lookup(16#80, 16#e) -> {more, 16#9c, 16#29}; -dec_huffman_lookup(16#80, 16#f) -> {ok, 16#9c, 16#38}; -dec_huffman_lookup(16#81, 16#0) -> {more, 16#a0, 16#03}; -dec_huffman_lookup(16#81, 16#1) -> {more, 16#a0, 16#06}; -dec_huffman_lookup(16#81, 16#2) -> {more, 16#a0, 16#0a}; -dec_huffman_lookup(16#81, 16#3) -> {more, 16#a0, 16#0f}; -dec_huffman_lookup(16#81, 16#4) -> {more, 16#a0, 16#18}; -dec_huffman_lookup(16#81, 16#5) -> {more, 16#a0, 16#1f}; -dec_huffman_lookup(16#81, 16#6) -> {more, 16#a0, 16#29}; -dec_huffman_lookup(16#81, 16#7) -> {ok, 16#a0, 16#38}; -dec_huffman_lookup(16#81, 16#8) -> {more, 16#a3, 16#03}; -dec_huffman_lookup(16#81, 16#9) -> {more, 16#a3, 16#06}; -dec_huffman_lookup(16#81, 16#a) -> {more, 16#a3, 16#0a}; -dec_huffman_lookup(16#81, 16#b) -> {more, 16#a3, 16#0f}; -dec_huffman_lookup(16#81, 16#c) -> {more, 16#a3, 16#18}; -dec_huffman_lookup(16#81, 16#d) -> {more, 16#a3, 16#1f}; -dec_huffman_lookup(16#81, 16#e) -> {more, 16#a3, 16#29}; -dec_huffman_lookup(16#81, 16#f) -> {ok, 16#a3, 16#38}; -dec_huffman_lookup(16#82, 16#0) -> {more, 16#a4, 16#02}; -dec_huffman_lookup(16#82, 16#1) -> {more, 16#a4, 16#09}; -dec_huffman_lookup(16#82, 16#2) -> {more, 16#a4, 16#17}; -dec_huffman_lookup(16#82, 16#3) -> {ok, 16#a4, 16#28}; -dec_huffman_lookup(16#82, 16#4) -> {more, 16#a9, 16#02}; -dec_huffman_lookup(16#82, 16#5) -> {more, 16#a9, 16#09}; -dec_huffman_lookup(16#82, 16#6) -> {more, 16#a9, 16#17}; -dec_huffman_lookup(16#82, 16#7) -> {ok, 16#a9, 16#28}; -dec_huffman_lookup(16#82, 16#8) -> {more, 16#aa, 16#02}; -dec_huffman_lookup(16#82, 16#9) -> {more, 16#aa, 16#09}; -dec_huffman_lookup(16#82, 16#a) -> {more, 16#aa, 16#17}; -dec_huffman_lookup(16#82, 16#b) -> {ok, 16#aa, 16#28}; -dec_huffman_lookup(16#82, 16#c) -> {more, 16#ad, 16#02}; -dec_huffman_lookup(16#82, 16#d) -> {more, 16#ad, 16#09}; -dec_huffman_lookup(16#82, 16#e) -> {more, 16#ad, 16#17}; -dec_huffman_lookup(16#82, 16#f) -> {ok, 16#ad, 16#28}; -dec_huffman_lookup(16#83, 16#0) -> {more, 16#a4, 16#03}; -dec_huffman_lookup(16#83, 16#1) -> {more, 16#a4, 16#06}; -dec_huffman_lookup(16#83, 16#2) -> {more, 16#a4, 16#0a}; -dec_huffman_lookup(16#83, 16#3) -> {more, 16#a4, 16#0f}; -dec_huffman_lookup(16#83, 16#4) -> {more, 16#a4, 16#18}; -dec_huffman_lookup(16#83, 16#5) -> {more, 16#a4, 16#1f}; -dec_huffman_lookup(16#83, 16#6) -> {more, 16#a4, 16#29}; -dec_huffman_lookup(16#83, 16#7) -> {ok, 16#a4, 16#38}; -dec_huffman_lookup(16#83, 16#8) -> {more, 16#a9, 16#03}; -dec_huffman_lookup(16#83, 16#9) -> {more, 16#a9, 16#06}; -dec_huffman_lookup(16#83, 16#a) -> {more, 16#a9, 16#0a}; -dec_huffman_lookup(16#83, 16#b) -> {more, 16#a9, 16#0f}; -dec_huffman_lookup(16#83, 16#c) -> {more, 16#a9, 16#18}; -dec_huffman_lookup(16#83, 16#d) -> {more, 16#a9, 16#1f}; -dec_huffman_lookup(16#83, 16#e) -> {more, 16#a9, 16#29}; -dec_huffman_lookup(16#83, 16#f) -> {ok, 16#a9, 16#38}; -dec_huffman_lookup(16#84, 16#0) -> {more, 16#aa, 16#03}; -dec_huffman_lookup(16#84, 16#1) -> {more, 16#aa, 16#06}; -dec_huffman_lookup(16#84, 16#2) -> {more, 16#aa, 16#0a}; -dec_huffman_lookup(16#84, 16#3) -> {more, 16#aa, 16#0f}; -dec_huffman_lookup(16#84, 16#4) -> {more, 16#aa, 16#18}; -dec_huffman_lookup(16#84, 16#5) -> {more, 16#aa, 16#1f}; -dec_huffman_lookup(16#84, 16#6) -> {more, 16#aa, 16#29}; -dec_huffman_lookup(16#84, 16#7) -> {ok, 16#aa, 16#38}; -dec_huffman_lookup(16#84, 16#8) -> {more, 16#ad, 16#03}; -dec_huffman_lookup(16#84, 16#9) -> {more, 16#ad, 16#06}; -dec_huffman_lookup(16#84, 16#a) -> {more, 16#ad, 16#0a}; -dec_huffman_lookup(16#84, 16#b) -> {more, 16#ad, 16#0f}; -dec_huffman_lookup(16#84, 16#c) -> {more, 16#ad, 16#18}; -dec_huffman_lookup(16#84, 16#d) -> {more, 16#ad, 16#1f}; -dec_huffman_lookup(16#84, 16#e) -> {more, 16#ad, 16#29}; -dec_huffman_lookup(16#84, 16#f) -> {ok, 16#ad, 16#38}; -dec_huffman_lookup(16#85, 16#0) -> {more, undefined, 16#89}; -dec_huffman_lookup(16#85, 16#1) -> {more, undefined, 16#8a}; -dec_huffman_lookup(16#85, 16#2) -> {more, undefined, 16#8c}; -dec_huffman_lookup(16#85, 16#3) -> {more, undefined, 16#8d}; -dec_huffman_lookup(16#85, 16#4) -> {more, undefined, 16#90}; -dec_huffman_lookup(16#85, 16#5) -> {more, undefined, 16#91}; -dec_huffman_lookup(16#85, 16#6) -> {more, undefined, 16#93}; -dec_huffman_lookup(16#85, 16#7) -> {more, undefined, 16#96}; -dec_huffman_lookup(16#85, 16#8) -> {more, undefined, 16#9c}; -dec_huffman_lookup(16#85, 16#9) -> {more, undefined, 16#9f}; -dec_huffman_lookup(16#85, 16#a) -> {more, undefined, 16#a3}; -dec_huffman_lookup(16#85, 16#b) -> {more, undefined, 16#a6}; -dec_huffman_lookup(16#85, 16#c) -> {more, undefined, 16#ab}; -dec_huffman_lookup(16#85, 16#d) -> {more, undefined, 16#ae}; -dec_huffman_lookup(16#85, 16#e) -> {more, undefined, 16#b5}; -dec_huffman_lookup(16#85, 16#f) -> {ok, undefined, 16#be}; -dec_huffman_lookup(16#86, 16#0) -> {ok, 16#b2, 16#00}; -dec_huffman_lookup(16#86, 16#1) -> {ok, 16#b5, 16#00}; -dec_huffman_lookup(16#86, 16#2) -> {ok, 16#b9, 16#00}; -dec_huffman_lookup(16#86, 16#3) -> {ok, 16#ba, 16#00}; -dec_huffman_lookup(16#86, 16#4) -> {ok, 16#bb, 16#00}; -dec_huffman_lookup(16#86, 16#5) -> {ok, 16#bd, 16#00}; -dec_huffman_lookup(16#86, 16#6) -> {ok, 16#be, 16#00}; -dec_huffman_lookup(16#86, 16#7) -> {ok, 16#c4, 16#00}; -dec_huffman_lookup(16#86, 16#8) -> {ok, 16#c6, 16#00}; -dec_huffman_lookup(16#86, 16#9) -> {ok, 16#e4, 16#00}; -dec_huffman_lookup(16#86, 16#a) -> {ok, 16#e8, 16#00}; -dec_huffman_lookup(16#86, 16#b) -> {ok, 16#e9, 16#00}; -dec_huffman_lookup(16#86, 16#c) -> {more, undefined, 16#94}; -dec_huffman_lookup(16#86, 16#d) -> {more, undefined, 16#95}; -dec_huffman_lookup(16#86, 16#e) -> {more, undefined, 16#97}; -dec_huffman_lookup(16#86, 16#f) -> {more, undefined, 16#98}; -dec_huffman_lookup(16#87, 16#0) -> {more, 16#b2, 16#01}; -dec_huffman_lookup(16#87, 16#1) -> {ok, 16#b2, 16#16}; -dec_huffman_lookup(16#87, 16#2) -> {more, 16#b5, 16#01}; -dec_huffman_lookup(16#87, 16#3) -> {ok, 16#b5, 16#16}; -dec_huffman_lookup(16#87, 16#4) -> {more, 16#b9, 16#01}; -dec_huffman_lookup(16#87, 16#5) -> {ok, 16#b9, 16#16}; -dec_huffman_lookup(16#87, 16#6) -> {more, 16#ba, 16#01}; -dec_huffman_lookup(16#87, 16#7) -> {ok, 16#ba, 16#16}; -dec_huffman_lookup(16#87, 16#8) -> {more, 16#bb, 16#01}; -dec_huffman_lookup(16#87, 16#9) -> {ok, 16#bb, 16#16}; -dec_huffman_lookup(16#87, 16#a) -> {more, 16#bd, 16#01}; -dec_huffman_lookup(16#87, 16#b) -> {ok, 16#bd, 16#16}; -dec_huffman_lookup(16#87, 16#c) -> {more, 16#be, 16#01}; -dec_huffman_lookup(16#87, 16#d) -> {ok, 16#be, 16#16}; -dec_huffman_lookup(16#87, 16#e) -> {more, 16#c4, 16#01}; -dec_huffman_lookup(16#87, 16#f) -> {ok, 16#c4, 16#16}; -dec_huffman_lookup(16#88, 16#0) -> {more, 16#b2, 16#02}; -dec_huffman_lookup(16#88, 16#1) -> {more, 16#b2, 16#09}; -dec_huffman_lookup(16#88, 16#2) -> {more, 16#b2, 16#17}; -dec_huffman_lookup(16#88, 16#3) -> {ok, 16#b2, 16#28}; -dec_huffman_lookup(16#88, 16#4) -> {more, 16#b5, 16#02}; -dec_huffman_lookup(16#88, 16#5) -> {more, 16#b5, 16#09}; -dec_huffman_lookup(16#88, 16#6) -> {more, 16#b5, 16#17}; -dec_huffman_lookup(16#88, 16#7) -> {ok, 16#b5, 16#28}; -dec_huffman_lookup(16#88, 16#8) -> {more, 16#b9, 16#02}; -dec_huffman_lookup(16#88, 16#9) -> {more, 16#b9, 16#09}; -dec_huffman_lookup(16#88, 16#a) -> {more, 16#b9, 16#17}; -dec_huffman_lookup(16#88, 16#b) -> {ok, 16#b9, 16#28}; -dec_huffman_lookup(16#88, 16#c) -> {more, 16#ba, 16#02}; -dec_huffman_lookup(16#88, 16#d) -> {more, 16#ba, 16#09}; -dec_huffman_lookup(16#88, 16#e) -> {more, 16#ba, 16#17}; -dec_huffman_lookup(16#88, 16#f) -> {ok, 16#ba, 16#28}; -dec_huffman_lookup(16#89, 16#0) -> {more, 16#b2, 16#03}; -dec_huffman_lookup(16#89, 16#1) -> {more, 16#b2, 16#06}; -dec_huffman_lookup(16#89, 16#2) -> {more, 16#b2, 16#0a}; -dec_huffman_lookup(16#89, 16#3) -> {more, 16#b2, 16#0f}; -dec_huffman_lookup(16#89, 16#4) -> {more, 16#b2, 16#18}; -dec_huffman_lookup(16#89, 16#5) -> {more, 16#b2, 16#1f}; -dec_huffman_lookup(16#89, 16#6) -> {more, 16#b2, 16#29}; -dec_huffman_lookup(16#89, 16#7) -> {ok, 16#b2, 16#38}; -dec_huffman_lookup(16#89, 16#8) -> {more, 16#b5, 16#03}; -dec_huffman_lookup(16#89, 16#9) -> {more, 16#b5, 16#06}; -dec_huffman_lookup(16#89, 16#a) -> {more, 16#b5, 16#0a}; -dec_huffman_lookup(16#89, 16#b) -> {more, 16#b5, 16#0f}; -dec_huffman_lookup(16#89, 16#c) -> {more, 16#b5, 16#18}; -dec_huffman_lookup(16#89, 16#d) -> {more, 16#b5, 16#1f}; -dec_huffman_lookup(16#89, 16#e) -> {more, 16#b5, 16#29}; -dec_huffman_lookup(16#89, 16#f) -> {ok, 16#b5, 16#38}; -dec_huffman_lookup(16#8a, 16#0) -> {more, 16#b9, 16#03}; -dec_huffman_lookup(16#8a, 16#1) -> {more, 16#b9, 16#06}; -dec_huffman_lookup(16#8a, 16#2) -> {more, 16#b9, 16#0a}; -dec_huffman_lookup(16#8a, 16#3) -> {more, 16#b9, 16#0f}; -dec_huffman_lookup(16#8a, 16#4) -> {more, 16#b9, 16#18}; -dec_huffman_lookup(16#8a, 16#5) -> {more, 16#b9, 16#1f}; -dec_huffman_lookup(16#8a, 16#6) -> {more, 16#b9, 16#29}; -dec_huffman_lookup(16#8a, 16#7) -> {ok, 16#b9, 16#38}; -dec_huffman_lookup(16#8a, 16#8) -> {more, 16#ba, 16#03}; -dec_huffman_lookup(16#8a, 16#9) -> {more, 16#ba, 16#06}; -dec_huffman_lookup(16#8a, 16#a) -> {more, 16#ba, 16#0a}; -dec_huffman_lookup(16#8a, 16#b) -> {more, 16#ba, 16#0f}; -dec_huffman_lookup(16#8a, 16#c) -> {more, 16#ba, 16#18}; -dec_huffman_lookup(16#8a, 16#d) -> {more, 16#ba, 16#1f}; -dec_huffman_lookup(16#8a, 16#e) -> {more, 16#ba, 16#29}; -dec_huffman_lookup(16#8a, 16#f) -> {ok, 16#ba, 16#38}; -dec_huffman_lookup(16#8b, 16#0) -> {more, 16#bb, 16#02}; -dec_huffman_lookup(16#8b, 16#1) -> {more, 16#bb, 16#09}; -dec_huffman_lookup(16#8b, 16#2) -> {more, 16#bb, 16#17}; -dec_huffman_lookup(16#8b, 16#3) -> {ok, 16#bb, 16#28}; -dec_huffman_lookup(16#8b, 16#4) -> {more, 16#bd, 16#02}; -dec_huffman_lookup(16#8b, 16#5) -> {more, 16#bd, 16#09}; -dec_huffman_lookup(16#8b, 16#6) -> {more, 16#bd, 16#17}; -dec_huffman_lookup(16#8b, 16#7) -> {ok, 16#bd, 16#28}; -dec_huffman_lookup(16#8b, 16#8) -> {more, 16#be, 16#02}; -dec_huffman_lookup(16#8b, 16#9) -> {more, 16#be, 16#09}; -dec_huffman_lookup(16#8b, 16#a) -> {more, 16#be, 16#17}; -dec_huffman_lookup(16#8b, 16#b) -> {ok, 16#be, 16#28}; -dec_huffman_lookup(16#8b, 16#c) -> {more, 16#c4, 16#02}; -dec_huffman_lookup(16#8b, 16#d) -> {more, 16#c4, 16#09}; -dec_huffman_lookup(16#8b, 16#e) -> {more, 16#c4, 16#17}; -dec_huffman_lookup(16#8b, 16#f) -> {ok, 16#c4, 16#28}; -dec_huffman_lookup(16#8c, 16#0) -> {more, 16#bb, 16#03}; -dec_huffman_lookup(16#8c, 16#1) -> {more, 16#bb, 16#06}; -dec_huffman_lookup(16#8c, 16#2) -> {more, 16#bb, 16#0a}; -dec_huffman_lookup(16#8c, 16#3) -> {more, 16#bb, 16#0f}; -dec_huffman_lookup(16#8c, 16#4) -> {more, 16#bb, 16#18}; -dec_huffman_lookup(16#8c, 16#5) -> {more, 16#bb, 16#1f}; -dec_huffman_lookup(16#8c, 16#6) -> {more, 16#bb, 16#29}; -dec_huffman_lookup(16#8c, 16#7) -> {ok, 16#bb, 16#38}; -dec_huffman_lookup(16#8c, 16#8) -> {more, 16#bd, 16#03}; -dec_huffman_lookup(16#8c, 16#9) -> {more, 16#bd, 16#06}; -dec_huffman_lookup(16#8c, 16#a) -> {more, 16#bd, 16#0a}; -dec_huffman_lookup(16#8c, 16#b) -> {more, 16#bd, 16#0f}; -dec_huffman_lookup(16#8c, 16#c) -> {more, 16#bd, 16#18}; -dec_huffman_lookup(16#8c, 16#d) -> {more, 16#bd, 16#1f}; -dec_huffman_lookup(16#8c, 16#e) -> {more, 16#bd, 16#29}; -dec_huffman_lookup(16#8c, 16#f) -> {ok, 16#bd, 16#38}; -dec_huffman_lookup(16#8d, 16#0) -> {more, 16#be, 16#03}; -dec_huffman_lookup(16#8d, 16#1) -> {more, 16#be, 16#06}; -dec_huffman_lookup(16#8d, 16#2) -> {more, 16#be, 16#0a}; -dec_huffman_lookup(16#8d, 16#3) -> {more, 16#be, 16#0f}; -dec_huffman_lookup(16#8d, 16#4) -> {more, 16#be, 16#18}; -dec_huffman_lookup(16#8d, 16#5) -> {more, 16#be, 16#1f}; -dec_huffman_lookup(16#8d, 16#6) -> {more, 16#be, 16#29}; -dec_huffman_lookup(16#8d, 16#7) -> {ok, 16#be, 16#38}; -dec_huffman_lookup(16#8d, 16#8) -> {more, 16#c4, 16#03}; -dec_huffman_lookup(16#8d, 16#9) -> {more, 16#c4, 16#06}; -dec_huffman_lookup(16#8d, 16#a) -> {more, 16#c4, 16#0a}; -dec_huffman_lookup(16#8d, 16#b) -> {more, 16#c4, 16#0f}; -dec_huffman_lookup(16#8d, 16#c) -> {more, 16#c4, 16#18}; -dec_huffman_lookup(16#8d, 16#d) -> {more, 16#c4, 16#1f}; -dec_huffman_lookup(16#8d, 16#e) -> {more, 16#c4, 16#29}; -dec_huffman_lookup(16#8d, 16#f) -> {ok, 16#c4, 16#38}; -dec_huffman_lookup(16#8e, 16#0) -> {more, 16#c6, 16#01}; -dec_huffman_lookup(16#8e, 16#1) -> {ok, 16#c6, 16#16}; -dec_huffman_lookup(16#8e, 16#2) -> {more, 16#e4, 16#01}; -dec_huffman_lookup(16#8e, 16#3) -> {ok, 16#e4, 16#16}; -dec_huffman_lookup(16#8e, 16#4) -> {more, 16#e8, 16#01}; -dec_huffman_lookup(16#8e, 16#5) -> {ok, 16#e8, 16#16}; -dec_huffman_lookup(16#8e, 16#6) -> {more, 16#e9, 16#01}; -dec_huffman_lookup(16#8e, 16#7) -> {ok, 16#e9, 16#16}; -dec_huffman_lookup(16#8e, 16#8) -> {ok, 16#01, 16#00}; -dec_huffman_lookup(16#8e, 16#9) -> {ok, 16#87, 16#00}; -dec_huffman_lookup(16#8e, 16#a) -> {ok, 16#89, 16#00}; -dec_huffman_lookup(16#8e, 16#b) -> {ok, 16#8a, 16#00}; -dec_huffman_lookup(16#8e, 16#c) -> {ok, 16#8b, 16#00}; -dec_huffman_lookup(16#8e, 16#d) -> {ok, 16#8c, 16#00}; -dec_huffman_lookup(16#8e, 16#e) -> {ok, 16#8d, 16#00}; -dec_huffman_lookup(16#8e, 16#f) -> {ok, 16#8f, 16#00}; -dec_huffman_lookup(16#8f, 16#0) -> {more, 16#c6, 16#02}; -dec_huffman_lookup(16#8f, 16#1) -> {more, 16#c6, 16#09}; -dec_huffman_lookup(16#8f, 16#2) -> {more, 16#c6, 16#17}; -dec_huffman_lookup(16#8f, 16#3) -> {ok, 16#c6, 16#28}; -dec_huffman_lookup(16#8f, 16#4) -> {more, 16#e4, 16#02}; -dec_huffman_lookup(16#8f, 16#5) -> {more, 16#e4, 16#09}; -dec_huffman_lookup(16#8f, 16#6) -> {more, 16#e4, 16#17}; -dec_huffman_lookup(16#8f, 16#7) -> {ok, 16#e4, 16#28}; -dec_huffman_lookup(16#8f, 16#8) -> {more, 16#e8, 16#02}; -dec_huffman_lookup(16#8f, 16#9) -> {more, 16#e8, 16#09}; -dec_huffman_lookup(16#8f, 16#a) -> {more, 16#e8, 16#17}; -dec_huffman_lookup(16#8f, 16#b) -> {ok, 16#e8, 16#28}; -dec_huffman_lookup(16#8f, 16#c) -> {more, 16#e9, 16#02}; -dec_huffman_lookup(16#8f, 16#d) -> {more, 16#e9, 16#09}; -dec_huffman_lookup(16#8f, 16#e) -> {more, 16#e9, 16#17}; -dec_huffman_lookup(16#8f, 16#f) -> {ok, 16#e9, 16#28}; -dec_huffman_lookup(16#90, 16#0) -> {more, 16#c6, 16#03}; -dec_huffman_lookup(16#90, 16#1) -> {more, 16#c6, 16#06}; -dec_huffman_lookup(16#90, 16#2) -> {more, 16#c6, 16#0a}; -dec_huffman_lookup(16#90, 16#3) -> {more, 16#c6, 16#0f}; -dec_huffman_lookup(16#90, 16#4) -> {more, 16#c6, 16#18}; -dec_huffman_lookup(16#90, 16#5) -> {more, 16#c6, 16#1f}; -dec_huffman_lookup(16#90, 16#6) -> {more, 16#c6, 16#29}; -dec_huffman_lookup(16#90, 16#7) -> {ok, 16#c6, 16#38}; -dec_huffman_lookup(16#90, 16#8) -> {more, 16#e4, 16#03}; -dec_huffman_lookup(16#90, 16#9) -> {more, 16#e4, 16#06}; -dec_huffman_lookup(16#90, 16#a) -> {more, 16#e4, 16#0a}; -dec_huffman_lookup(16#90, 16#b) -> {more, 16#e4, 16#0f}; -dec_huffman_lookup(16#90, 16#c) -> {more, 16#e4, 16#18}; -dec_huffman_lookup(16#90, 16#d) -> {more, 16#e4, 16#1f}; -dec_huffman_lookup(16#90, 16#e) -> {more, 16#e4, 16#29}; -dec_huffman_lookup(16#90, 16#f) -> {ok, 16#e4, 16#38}; -dec_huffman_lookup(16#91, 16#0) -> {more, 16#e8, 16#03}; -dec_huffman_lookup(16#91, 16#1) -> {more, 16#e8, 16#06}; -dec_huffman_lookup(16#91, 16#2) -> {more, 16#e8, 16#0a}; -dec_huffman_lookup(16#91, 16#3) -> {more, 16#e8, 16#0f}; -dec_huffman_lookup(16#91, 16#4) -> {more, 16#e8, 16#18}; -dec_huffman_lookup(16#91, 16#5) -> {more, 16#e8, 16#1f}; -dec_huffman_lookup(16#91, 16#6) -> {more, 16#e8, 16#29}; -dec_huffman_lookup(16#91, 16#7) -> {ok, 16#e8, 16#38}; -dec_huffman_lookup(16#91, 16#8) -> {more, 16#e9, 16#03}; -dec_huffman_lookup(16#91, 16#9) -> {more, 16#e9, 16#06}; -dec_huffman_lookup(16#91, 16#a) -> {more, 16#e9, 16#0a}; -dec_huffman_lookup(16#91, 16#b) -> {more, 16#e9, 16#0f}; -dec_huffman_lookup(16#91, 16#c) -> {more, 16#e9, 16#18}; -dec_huffman_lookup(16#91, 16#d) -> {more, 16#e9, 16#1f}; -dec_huffman_lookup(16#91, 16#e) -> {more, 16#e9, 16#29}; -dec_huffman_lookup(16#91, 16#f) -> {ok, 16#e9, 16#38}; -dec_huffman_lookup(16#92, 16#0) -> {more, 16#01, 16#01}; -dec_huffman_lookup(16#92, 16#1) -> {ok, 16#01, 16#16}; -dec_huffman_lookup(16#92, 16#2) -> {more, 16#87, 16#01}; -dec_huffman_lookup(16#92, 16#3) -> {ok, 16#87, 16#16}; -dec_huffman_lookup(16#92, 16#4) -> {more, 16#89, 16#01}; -dec_huffman_lookup(16#92, 16#5) -> {ok, 16#89, 16#16}; -dec_huffman_lookup(16#92, 16#6) -> {more, 16#8a, 16#01}; -dec_huffman_lookup(16#92, 16#7) -> {ok, 16#8a, 16#16}; -dec_huffman_lookup(16#92, 16#8) -> {more, 16#8b, 16#01}; -dec_huffman_lookup(16#92, 16#9) -> {ok, 16#8b, 16#16}; -dec_huffman_lookup(16#92, 16#a) -> {more, 16#8c, 16#01}; -dec_huffman_lookup(16#92, 16#b) -> {ok, 16#8c, 16#16}; -dec_huffman_lookup(16#92, 16#c) -> {more, 16#8d, 16#01}; -dec_huffman_lookup(16#92, 16#d) -> {ok, 16#8d, 16#16}; -dec_huffman_lookup(16#92, 16#e) -> {more, 16#8f, 16#01}; -dec_huffman_lookup(16#92, 16#f) -> {ok, 16#8f, 16#16}; -dec_huffman_lookup(16#93, 16#0) -> {more, 16#01, 16#02}; -dec_huffman_lookup(16#93, 16#1) -> {more, 16#01, 16#09}; -dec_huffman_lookup(16#93, 16#2) -> {more, 16#01, 16#17}; -dec_huffman_lookup(16#93, 16#3) -> {ok, 16#01, 16#28}; -dec_huffman_lookup(16#93, 16#4) -> {more, 16#87, 16#02}; -dec_huffman_lookup(16#93, 16#5) -> {more, 16#87, 16#09}; -dec_huffman_lookup(16#93, 16#6) -> {more, 16#87, 16#17}; -dec_huffman_lookup(16#93, 16#7) -> {ok, 16#87, 16#28}; -dec_huffman_lookup(16#93, 16#8) -> {more, 16#89, 16#02}; -dec_huffman_lookup(16#93, 16#9) -> {more, 16#89, 16#09}; -dec_huffman_lookup(16#93, 16#a) -> {more, 16#89, 16#17}; -dec_huffman_lookup(16#93, 16#b) -> {ok, 16#89, 16#28}; -dec_huffman_lookup(16#93, 16#c) -> {more, 16#8a, 16#02}; -dec_huffman_lookup(16#93, 16#d) -> {more, 16#8a, 16#09}; -dec_huffman_lookup(16#93, 16#e) -> {more, 16#8a, 16#17}; -dec_huffman_lookup(16#93, 16#f) -> {ok, 16#8a, 16#28}; -dec_huffman_lookup(16#94, 16#0) -> {more, 16#01, 16#03}; -dec_huffman_lookup(16#94, 16#1) -> {more, 16#01, 16#06}; -dec_huffman_lookup(16#94, 16#2) -> {more, 16#01, 16#0a}; -dec_huffman_lookup(16#94, 16#3) -> {more, 16#01, 16#0f}; -dec_huffman_lookup(16#94, 16#4) -> {more, 16#01, 16#18}; -dec_huffman_lookup(16#94, 16#5) -> {more, 16#01, 16#1f}; -dec_huffman_lookup(16#94, 16#6) -> {more, 16#01, 16#29}; -dec_huffman_lookup(16#94, 16#7) -> {ok, 16#01, 16#38}; -dec_huffman_lookup(16#94, 16#8) -> {more, 16#87, 16#03}; -dec_huffman_lookup(16#94, 16#9) -> {more, 16#87, 16#06}; -dec_huffman_lookup(16#94, 16#a) -> {more, 16#87, 16#0a}; -dec_huffman_lookup(16#94, 16#b) -> {more, 16#87, 16#0f}; -dec_huffman_lookup(16#94, 16#c) -> {more, 16#87, 16#18}; -dec_huffman_lookup(16#94, 16#d) -> {more, 16#87, 16#1f}; -dec_huffman_lookup(16#94, 16#e) -> {more, 16#87, 16#29}; -dec_huffman_lookup(16#94, 16#f) -> {ok, 16#87, 16#38}; -dec_huffman_lookup(16#95, 16#0) -> {more, 16#89, 16#03}; -dec_huffman_lookup(16#95, 16#1) -> {more, 16#89, 16#06}; -dec_huffman_lookup(16#95, 16#2) -> {more, 16#89, 16#0a}; -dec_huffman_lookup(16#95, 16#3) -> {more, 16#89, 16#0f}; -dec_huffman_lookup(16#95, 16#4) -> {more, 16#89, 16#18}; -dec_huffman_lookup(16#95, 16#5) -> {more, 16#89, 16#1f}; -dec_huffman_lookup(16#95, 16#6) -> {more, 16#89, 16#29}; -dec_huffman_lookup(16#95, 16#7) -> {ok, 16#89, 16#38}; -dec_huffman_lookup(16#95, 16#8) -> {more, 16#8a, 16#03}; -dec_huffman_lookup(16#95, 16#9) -> {more, 16#8a, 16#06}; -dec_huffman_lookup(16#95, 16#a) -> {more, 16#8a, 16#0a}; -dec_huffman_lookup(16#95, 16#b) -> {more, 16#8a, 16#0f}; -dec_huffman_lookup(16#95, 16#c) -> {more, 16#8a, 16#18}; -dec_huffman_lookup(16#95, 16#d) -> {more, 16#8a, 16#1f}; -dec_huffman_lookup(16#95, 16#e) -> {more, 16#8a, 16#29}; -dec_huffman_lookup(16#95, 16#f) -> {ok, 16#8a, 16#38}; -dec_huffman_lookup(16#96, 16#0) -> {more, 16#8b, 16#02}; -dec_huffman_lookup(16#96, 16#1) -> {more, 16#8b, 16#09}; -dec_huffman_lookup(16#96, 16#2) -> {more, 16#8b, 16#17}; -dec_huffman_lookup(16#96, 16#3) -> {ok, 16#8b, 16#28}; -dec_huffman_lookup(16#96, 16#4) -> {more, 16#8c, 16#02}; -dec_huffman_lookup(16#96, 16#5) -> {more, 16#8c, 16#09}; -dec_huffman_lookup(16#96, 16#6) -> {more, 16#8c, 16#17}; -dec_huffman_lookup(16#96, 16#7) -> {ok, 16#8c, 16#28}; -dec_huffman_lookup(16#96, 16#8) -> {more, 16#8d, 16#02}; -dec_huffman_lookup(16#96, 16#9) -> {more, 16#8d, 16#09}; -dec_huffman_lookup(16#96, 16#a) -> {more, 16#8d, 16#17}; -dec_huffman_lookup(16#96, 16#b) -> {ok, 16#8d, 16#28}; -dec_huffman_lookup(16#96, 16#c) -> {more, 16#8f, 16#02}; -dec_huffman_lookup(16#96, 16#d) -> {more, 16#8f, 16#09}; -dec_huffman_lookup(16#96, 16#e) -> {more, 16#8f, 16#17}; -dec_huffman_lookup(16#96, 16#f) -> {ok, 16#8f, 16#28}; -dec_huffman_lookup(16#97, 16#0) -> {more, 16#8b, 16#03}; -dec_huffman_lookup(16#97, 16#1) -> {more, 16#8b, 16#06}; -dec_huffman_lookup(16#97, 16#2) -> {more, 16#8b, 16#0a}; -dec_huffman_lookup(16#97, 16#3) -> {more, 16#8b, 16#0f}; -dec_huffman_lookup(16#97, 16#4) -> {more, 16#8b, 16#18}; -dec_huffman_lookup(16#97, 16#5) -> {more, 16#8b, 16#1f}; -dec_huffman_lookup(16#97, 16#6) -> {more, 16#8b, 16#29}; -dec_huffman_lookup(16#97, 16#7) -> {ok, 16#8b, 16#38}; -dec_huffman_lookup(16#97, 16#8) -> {more, 16#8c, 16#03}; -dec_huffman_lookup(16#97, 16#9) -> {more, 16#8c, 16#06}; -dec_huffman_lookup(16#97, 16#a) -> {more, 16#8c, 16#0a}; -dec_huffman_lookup(16#97, 16#b) -> {more, 16#8c, 16#0f}; -dec_huffman_lookup(16#97, 16#c) -> {more, 16#8c, 16#18}; -dec_huffman_lookup(16#97, 16#d) -> {more, 16#8c, 16#1f}; -dec_huffman_lookup(16#97, 16#e) -> {more, 16#8c, 16#29}; -dec_huffman_lookup(16#97, 16#f) -> {ok, 16#8c, 16#38}; -dec_huffman_lookup(16#98, 16#0) -> {more, 16#8d, 16#03}; -dec_huffman_lookup(16#98, 16#1) -> {more, 16#8d, 16#06}; -dec_huffman_lookup(16#98, 16#2) -> {more, 16#8d, 16#0a}; -dec_huffman_lookup(16#98, 16#3) -> {more, 16#8d, 16#0f}; -dec_huffman_lookup(16#98, 16#4) -> {more, 16#8d, 16#18}; -dec_huffman_lookup(16#98, 16#5) -> {more, 16#8d, 16#1f}; -dec_huffman_lookup(16#98, 16#6) -> {more, 16#8d, 16#29}; -dec_huffman_lookup(16#98, 16#7) -> {ok, 16#8d, 16#38}; -dec_huffman_lookup(16#98, 16#8) -> {more, 16#8f, 16#03}; -dec_huffman_lookup(16#98, 16#9) -> {more, 16#8f, 16#06}; -dec_huffman_lookup(16#98, 16#a) -> {more, 16#8f, 16#0a}; -dec_huffman_lookup(16#98, 16#b) -> {more, 16#8f, 16#0f}; -dec_huffman_lookup(16#98, 16#c) -> {more, 16#8f, 16#18}; -dec_huffman_lookup(16#98, 16#d) -> {more, 16#8f, 16#1f}; -dec_huffman_lookup(16#98, 16#e) -> {more, 16#8f, 16#29}; -dec_huffman_lookup(16#98, 16#f) -> {ok, 16#8f, 16#38}; -dec_huffman_lookup(16#99, 16#0) -> {more, undefined, 16#9d}; -dec_huffman_lookup(16#99, 16#1) -> {more, undefined, 16#9e}; -dec_huffman_lookup(16#99, 16#2) -> {more, undefined, 16#a0}; -dec_huffman_lookup(16#99, 16#3) -> {more, undefined, 16#a1}; -dec_huffman_lookup(16#99, 16#4) -> {more, undefined, 16#a4}; -dec_huffman_lookup(16#99, 16#5) -> {more, undefined, 16#a5}; -dec_huffman_lookup(16#99, 16#6) -> {more, undefined, 16#a7}; -dec_huffman_lookup(16#99, 16#7) -> {more, undefined, 16#a8}; -dec_huffman_lookup(16#99, 16#8) -> {more, undefined, 16#ac}; -dec_huffman_lookup(16#99, 16#9) -> {more, undefined, 16#ad}; -dec_huffman_lookup(16#99, 16#a) -> {more, undefined, 16#af}; -dec_huffman_lookup(16#99, 16#b) -> {more, undefined, 16#b1}; -dec_huffman_lookup(16#99, 16#c) -> {more, undefined, 16#b6}; -dec_huffman_lookup(16#99, 16#d) -> {more, undefined, 16#b9}; -dec_huffman_lookup(16#99, 16#e) -> {more, undefined, 16#bf}; -dec_huffman_lookup(16#99, 16#f) -> {ok, undefined, 16#cf}; -dec_huffman_lookup(16#9a, 16#0) -> {ok, 16#93, 16#00}; -dec_huffman_lookup(16#9a, 16#1) -> {ok, 16#95, 16#00}; -dec_huffman_lookup(16#9a, 16#2) -> {ok, 16#96, 16#00}; -dec_huffman_lookup(16#9a, 16#3) -> {ok, 16#97, 16#00}; -dec_huffman_lookup(16#9a, 16#4) -> {ok, 16#98, 16#00}; -dec_huffman_lookup(16#9a, 16#5) -> {ok, 16#9b, 16#00}; -dec_huffman_lookup(16#9a, 16#6) -> {ok, 16#9d, 16#00}; -dec_huffman_lookup(16#9a, 16#7) -> {ok, 16#9e, 16#00}; -dec_huffman_lookup(16#9a, 16#8) -> {ok, 16#a5, 16#00}; -dec_huffman_lookup(16#9a, 16#9) -> {ok, 16#a6, 16#00}; -dec_huffman_lookup(16#9a, 16#a) -> {ok, 16#a8, 16#00}; -dec_huffman_lookup(16#9a, 16#b) -> {ok, 16#ae, 16#00}; -dec_huffman_lookup(16#9a, 16#c) -> {ok, 16#af, 16#00}; -dec_huffman_lookup(16#9a, 16#d) -> {ok, 16#b4, 16#00}; -dec_huffman_lookup(16#9a, 16#e) -> {ok, 16#b6, 16#00}; -dec_huffman_lookup(16#9a, 16#f) -> {ok, 16#b7, 16#00}; -dec_huffman_lookup(16#9b, 16#0) -> {more, 16#93, 16#01}; -dec_huffman_lookup(16#9b, 16#1) -> {ok, 16#93, 16#16}; -dec_huffman_lookup(16#9b, 16#2) -> {more, 16#95, 16#01}; -dec_huffman_lookup(16#9b, 16#3) -> {ok, 16#95, 16#16}; -dec_huffman_lookup(16#9b, 16#4) -> {more, 16#96, 16#01}; -dec_huffman_lookup(16#9b, 16#5) -> {ok, 16#96, 16#16}; -dec_huffman_lookup(16#9b, 16#6) -> {more, 16#97, 16#01}; -dec_huffman_lookup(16#9b, 16#7) -> {ok, 16#97, 16#16}; -dec_huffman_lookup(16#9b, 16#8) -> {more, 16#98, 16#01}; -dec_huffman_lookup(16#9b, 16#9) -> {ok, 16#98, 16#16}; -dec_huffman_lookup(16#9b, 16#a) -> {more, 16#9b, 16#01}; -dec_huffman_lookup(16#9b, 16#b) -> {ok, 16#9b, 16#16}; -dec_huffman_lookup(16#9b, 16#c) -> {more, 16#9d, 16#01}; -dec_huffman_lookup(16#9b, 16#d) -> {ok, 16#9d, 16#16}; -dec_huffman_lookup(16#9b, 16#e) -> {more, 16#9e, 16#01}; -dec_huffman_lookup(16#9b, 16#f) -> {ok, 16#9e, 16#16}; -dec_huffman_lookup(16#9c, 16#0) -> {more, 16#93, 16#02}; -dec_huffman_lookup(16#9c, 16#1) -> {more, 16#93, 16#09}; -dec_huffman_lookup(16#9c, 16#2) -> {more, 16#93, 16#17}; -dec_huffman_lookup(16#9c, 16#3) -> {ok, 16#93, 16#28}; -dec_huffman_lookup(16#9c, 16#4) -> {more, 16#95, 16#02}; -dec_huffman_lookup(16#9c, 16#5) -> {more, 16#95, 16#09}; -dec_huffman_lookup(16#9c, 16#6) -> {more, 16#95, 16#17}; -dec_huffman_lookup(16#9c, 16#7) -> {ok, 16#95, 16#28}; -dec_huffman_lookup(16#9c, 16#8) -> {more, 16#96, 16#02}; -dec_huffman_lookup(16#9c, 16#9) -> {more, 16#96, 16#09}; -dec_huffman_lookup(16#9c, 16#a) -> {more, 16#96, 16#17}; -dec_huffman_lookup(16#9c, 16#b) -> {ok, 16#96, 16#28}; -dec_huffman_lookup(16#9c, 16#c) -> {more, 16#97, 16#02}; -dec_huffman_lookup(16#9c, 16#d) -> {more, 16#97, 16#09}; -dec_huffman_lookup(16#9c, 16#e) -> {more, 16#97, 16#17}; -dec_huffman_lookup(16#9c, 16#f) -> {ok, 16#97, 16#28}; -dec_huffman_lookup(16#9d, 16#0) -> {more, 16#93, 16#03}; -dec_huffman_lookup(16#9d, 16#1) -> {more, 16#93, 16#06}; -dec_huffman_lookup(16#9d, 16#2) -> {more, 16#93, 16#0a}; -dec_huffman_lookup(16#9d, 16#3) -> {more, 16#93, 16#0f}; -dec_huffman_lookup(16#9d, 16#4) -> {more, 16#93, 16#18}; -dec_huffman_lookup(16#9d, 16#5) -> {more, 16#93, 16#1f}; -dec_huffman_lookup(16#9d, 16#6) -> {more, 16#93, 16#29}; -dec_huffman_lookup(16#9d, 16#7) -> {ok, 16#93, 16#38}; -dec_huffman_lookup(16#9d, 16#8) -> {more, 16#95, 16#03}; -dec_huffman_lookup(16#9d, 16#9) -> {more, 16#95, 16#06}; -dec_huffman_lookup(16#9d, 16#a) -> {more, 16#95, 16#0a}; -dec_huffman_lookup(16#9d, 16#b) -> {more, 16#95, 16#0f}; -dec_huffman_lookup(16#9d, 16#c) -> {more, 16#95, 16#18}; -dec_huffman_lookup(16#9d, 16#d) -> {more, 16#95, 16#1f}; -dec_huffman_lookup(16#9d, 16#e) -> {more, 16#95, 16#29}; -dec_huffman_lookup(16#9d, 16#f) -> {ok, 16#95, 16#38}; -dec_huffman_lookup(16#9e, 16#0) -> {more, 16#96, 16#03}; -dec_huffman_lookup(16#9e, 16#1) -> {more, 16#96, 16#06}; -dec_huffman_lookup(16#9e, 16#2) -> {more, 16#96, 16#0a}; -dec_huffman_lookup(16#9e, 16#3) -> {more, 16#96, 16#0f}; -dec_huffman_lookup(16#9e, 16#4) -> {more, 16#96, 16#18}; -dec_huffman_lookup(16#9e, 16#5) -> {more, 16#96, 16#1f}; -dec_huffman_lookup(16#9e, 16#6) -> {more, 16#96, 16#29}; -dec_huffman_lookup(16#9e, 16#7) -> {ok, 16#96, 16#38}; -dec_huffman_lookup(16#9e, 16#8) -> {more, 16#97, 16#03}; -dec_huffman_lookup(16#9e, 16#9) -> {more, 16#97, 16#06}; -dec_huffman_lookup(16#9e, 16#a) -> {more, 16#97, 16#0a}; -dec_huffman_lookup(16#9e, 16#b) -> {more, 16#97, 16#0f}; -dec_huffman_lookup(16#9e, 16#c) -> {more, 16#97, 16#18}; -dec_huffman_lookup(16#9e, 16#d) -> {more, 16#97, 16#1f}; -dec_huffman_lookup(16#9e, 16#e) -> {more, 16#97, 16#29}; -dec_huffman_lookup(16#9e, 16#f) -> {ok, 16#97, 16#38}; -dec_huffman_lookup(16#9f, 16#0) -> {more, 16#98, 16#02}; -dec_huffman_lookup(16#9f, 16#1) -> {more, 16#98, 16#09}; -dec_huffman_lookup(16#9f, 16#2) -> {more, 16#98, 16#17}; -dec_huffman_lookup(16#9f, 16#3) -> {ok, 16#98, 16#28}; -dec_huffman_lookup(16#9f, 16#4) -> {more, 16#9b, 16#02}; -dec_huffman_lookup(16#9f, 16#5) -> {more, 16#9b, 16#09}; -dec_huffman_lookup(16#9f, 16#6) -> {more, 16#9b, 16#17}; -dec_huffman_lookup(16#9f, 16#7) -> {ok, 16#9b, 16#28}; -dec_huffman_lookup(16#9f, 16#8) -> {more, 16#9d, 16#02}; -dec_huffman_lookup(16#9f, 16#9) -> {more, 16#9d, 16#09}; -dec_huffman_lookup(16#9f, 16#a) -> {more, 16#9d, 16#17}; -dec_huffman_lookup(16#9f, 16#b) -> {ok, 16#9d, 16#28}; -dec_huffman_lookup(16#9f, 16#c) -> {more, 16#9e, 16#02}; -dec_huffman_lookup(16#9f, 16#d) -> {more, 16#9e, 16#09}; -dec_huffman_lookup(16#9f, 16#e) -> {more, 16#9e, 16#17}; -dec_huffman_lookup(16#9f, 16#f) -> {ok, 16#9e, 16#28}; -dec_huffman_lookup(16#a0, 16#0) -> {more, 16#98, 16#03}; -dec_huffman_lookup(16#a0, 16#1) -> {more, 16#98, 16#06}; -dec_huffman_lookup(16#a0, 16#2) -> {more, 16#98, 16#0a}; -dec_huffman_lookup(16#a0, 16#3) -> {more, 16#98, 16#0f}; -dec_huffman_lookup(16#a0, 16#4) -> {more, 16#98, 16#18}; -dec_huffman_lookup(16#a0, 16#5) -> {more, 16#98, 16#1f}; -dec_huffman_lookup(16#a0, 16#6) -> {more, 16#98, 16#29}; -dec_huffman_lookup(16#a0, 16#7) -> {ok, 16#98, 16#38}; -dec_huffman_lookup(16#a0, 16#8) -> {more, 16#9b, 16#03}; -dec_huffman_lookup(16#a0, 16#9) -> {more, 16#9b, 16#06}; -dec_huffman_lookup(16#a0, 16#a) -> {more, 16#9b, 16#0a}; -dec_huffman_lookup(16#a0, 16#b) -> {more, 16#9b, 16#0f}; -dec_huffman_lookup(16#a0, 16#c) -> {more, 16#9b, 16#18}; -dec_huffman_lookup(16#a0, 16#d) -> {more, 16#9b, 16#1f}; -dec_huffman_lookup(16#a0, 16#e) -> {more, 16#9b, 16#29}; -dec_huffman_lookup(16#a0, 16#f) -> {ok, 16#9b, 16#38}; -dec_huffman_lookup(16#a1, 16#0) -> {more, 16#9d, 16#03}; -dec_huffman_lookup(16#a1, 16#1) -> {more, 16#9d, 16#06}; -dec_huffman_lookup(16#a1, 16#2) -> {more, 16#9d, 16#0a}; -dec_huffman_lookup(16#a1, 16#3) -> {more, 16#9d, 16#0f}; -dec_huffman_lookup(16#a1, 16#4) -> {more, 16#9d, 16#18}; -dec_huffman_lookup(16#a1, 16#5) -> {more, 16#9d, 16#1f}; -dec_huffman_lookup(16#a1, 16#6) -> {more, 16#9d, 16#29}; -dec_huffman_lookup(16#a1, 16#7) -> {ok, 16#9d, 16#38}; -dec_huffman_lookup(16#a1, 16#8) -> {more, 16#9e, 16#03}; -dec_huffman_lookup(16#a1, 16#9) -> {more, 16#9e, 16#06}; -dec_huffman_lookup(16#a1, 16#a) -> {more, 16#9e, 16#0a}; -dec_huffman_lookup(16#a1, 16#b) -> {more, 16#9e, 16#0f}; -dec_huffman_lookup(16#a1, 16#c) -> {more, 16#9e, 16#18}; -dec_huffman_lookup(16#a1, 16#d) -> {more, 16#9e, 16#1f}; -dec_huffman_lookup(16#a1, 16#e) -> {more, 16#9e, 16#29}; -dec_huffman_lookup(16#a1, 16#f) -> {ok, 16#9e, 16#38}; -dec_huffman_lookup(16#a2, 16#0) -> {more, 16#a5, 16#01}; -dec_huffman_lookup(16#a2, 16#1) -> {ok, 16#a5, 16#16}; -dec_huffman_lookup(16#a2, 16#2) -> {more, 16#a6, 16#01}; -dec_huffman_lookup(16#a2, 16#3) -> {ok, 16#a6, 16#16}; -dec_huffman_lookup(16#a2, 16#4) -> {more, 16#a8, 16#01}; -dec_huffman_lookup(16#a2, 16#5) -> {ok, 16#a8, 16#16}; -dec_huffman_lookup(16#a2, 16#6) -> {more, 16#ae, 16#01}; -dec_huffman_lookup(16#a2, 16#7) -> {ok, 16#ae, 16#16}; -dec_huffman_lookup(16#a2, 16#8) -> {more, 16#af, 16#01}; -dec_huffman_lookup(16#a2, 16#9) -> {ok, 16#af, 16#16}; -dec_huffman_lookup(16#a2, 16#a) -> {more, 16#b4, 16#01}; -dec_huffman_lookup(16#a2, 16#b) -> {ok, 16#b4, 16#16}; -dec_huffman_lookup(16#a2, 16#c) -> {more, 16#b6, 16#01}; -dec_huffman_lookup(16#a2, 16#d) -> {ok, 16#b6, 16#16}; -dec_huffman_lookup(16#a2, 16#e) -> {more, 16#b7, 16#01}; -dec_huffman_lookup(16#a2, 16#f) -> {ok, 16#b7, 16#16}; -dec_huffman_lookup(16#a3, 16#0) -> {more, 16#a5, 16#02}; -dec_huffman_lookup(16#a3, 16#1) -> {more, 16#a5, 16#09}; -dec_huffman_lookup(16#a3, 16#2) -> {more, 16#a5, 16#17}; -dec_huffman_lookup(16#a3, 16#3) -> {ok, 16#a5, 16#28}; -dec_huffman_lookup(16#a3, 16#4) -> {more, 16#a6, 16#02}; -dec_huffman_lookup(16#a3, 16#5) -> {more, 16#a6, 16#09}; -dec_huffman_lookup(16#a3, 16#6) -> {more, 16#a6, 16#17}; -dec_huffman_lookup(16#a3, 16#7) -> {ok, 16#a6, 16#28}; -dec_huffman_lookup(16#a3, 16#8) -> {more, 16#a8, 16#02}; -dec_huffman_lookup(16#a3, 16#9) -> {more, 16#a8, 16#09}; -dec_huffman_lookup(16#a3, 16#a) -> {more, 16#a8, 16#17}; -dec_huffman_lookup(16#a3, 16#b) -> {ok, 16#a8, 16#28}; -dec_huffman_lookup(16#a3, 16#c) -> {more, 16#ae, 16#02}; -dec_huffman_lookup(16#a3, 16#d) -> {more, 16#ae, 16#09}; -dec_huffman_lookup(16#a3, 16#e) -> {more, 16#ae, 16#17}; -dec_huffman_lookup(16#a3, 16#f) -> {ok, 16#ae, 16#28}; -dec_huffman_lookup(16#a4, 16#0) -> {more, 16#a5, 16#03}; -dec_huffman_lookup(16#a4, 16#1) -> {more, 16#a5, 16#06}; -dec_huffman_lookup(16#a4, 16#2) -> {more, 16#a5, 16#0a}; -dec_huffman_lookup(16#a4, 16#3) -> {more, 16#a5, 16#0f}; -dec_huffman_lookup(16#a4, 16#4) -> {more, 16#a5, 16#18}; -dec_huffman_lookup(16#a4, 16#5) -> {more, 16#a5, 16#1f}; -dec_huffman_lookup(16#a4, 16#6) -> {more, 16#a5, 16#29}; -dec_huffman_lookup(16#a4, 16#7) -> {ok, 16#a5, 16#38}; -dec_huffman_lookup(16#a4, 16#8) -> {more, 16#a6, 16#03}; -dec_huffman_lookup(16#a4, 16#9) -> {more, 16#a6, 16#06}; -dec_huffman_lookup(16#a4, 16#a) -> {more, 16#a6, 16#0a}; -dec_huffman_lookup(16#a4, 16#b) -> {more, 16#a6, 16#0f}; -dec_huffman_lookup(16#a4, 16#c) -> {more, 16#a6, 16#18}; -dec_huffman_lookup(16#a4, 16#d) -> {more, 16#a6, 16#1f}; -dec_huffman_lookup(16#a4, 16#e) -> {more, 16#a6, 16#29}; -dec_huffman_lookup(16#a4, 16#f) -> {ok, 16#a6, 16#38}; -dec_huffman_lookup(16#a5, 16#0) -> {more, 16#a8, 16#03}; -dec_huffman_lookup(16#a5, 16#1) -> {more, 16#a8, 16#06}; -dec_huffman_lookup(16#a5, 16#2) -> {more, 16#a8, 16#0a}; -dec_huffman_lookup(16#a5, 16#3) -> {more, 16#a8, 16#0f}; -dec_huffman_lookup(16#a5, 16#4) -> {more, 16#a8, 16#18}; -dec_huffman_lookup(16#a5, 16#5) -> {more, 16#a8, 16#1f}; -dec_huffman_lookup(16#a5, 16#6) -> {more, 16#a8, 16#29}; -dec_huffman_lookup(16#a5, 16#7) -> {ok, 16#a8, 16#38}; -dec_huffman_lookup(16#a5, 16#8) -> {more, 16#ae, 16#03}; -dec_huffman_lookup(16#a5, 16#9) -> {more, 16#ae, 16#06}; -dec_huffman_lookup(16#a5, 16#a) -> {more, 16#ae, 16#0a}; -dec_huffman_lookup(16#a5, 16#b) -> {more, 16#ae, 16#0f}; -dec_huffman_lookup(16#a5, 16#c) -> {more, 16#ae, 16#18}; -dec_huffman_lookup(16#a5, 16#d) -> {more, 16#ae, 16#1f}; -dec_huffman_lookup(16#a5, 16#e) -> {more, 16#ae, 16#29}; -dec_huffman_lookup(16#a5, 16#f) -> {ok, 16#ae, 16#38}; -dec_huffman_lookup(16#a6, 16#0) -> {more, 16#af, 16#02}; -dec_huffman_lookup(16#a6, 16#1) -> {more, 16#af, 16#09}; -dec_huffman_lookup(16#a6, 16#2) -> {more, 16#af, 16#17}; -dec_huffman_lookup(16#a6, 16#3) -> {ok, 16#af, 16#28}; -dec_huffman_lookup(16#a6, 16#4) -> {more, 16#b4, 16#02}; -dec_huffman_lookup(16#a6, 16#5) -> {more, 16#b4, 16#09}; -dec_huffman_lookup(16#a6, 16#6) -> {more, 16#b4, 16#17}; -dec_huffman_lookup(16#a6, 16#7) -> {ok, 16#b4, 16#28}; -dec_huffman_lookup(16#a6, 16#8) -> {more, 16#b6, 16#02}; -dec_huffman_lookup(16#a6, 16#9) -> {more, 16#b6, 16#09}; -dec_huffman_lookup(16#a6, 16#a) -> {more, 16#b6, 16#17}; -dec_huffman_lookup(16#a6, 16#b) -> {ok, 16#b6, 16#28}; -dec_huffman_lookup(16#a6, 16#c) -> {more, 16#b7, 16#02}; -dec_huffman_lookup(16#a6, 16#d) -> {more, 16#b7, 16#09}; -dec_huffman_lookup(16#a6, 16#e) -> {more, 16#b7, 16#17}; -dec_huffman_lookup(16#a6, 16#f) -> {ok, 16#b7, 16#28}; -dec_huffman_lookup(16#a7, 16#0) -> {more, 16#af, 16#03}; -dec_huffman_lookup(16#a7, 16#1) -> {more, 16#af, 16#06}; -dec_huffman_lookup(16#a7, 16#2) -> {more, 16#af, 16#0a}; -dec_huffman_lookup(16#a7, 16#3) -> {more, 16#af, 16#0f}; -dec_huffman_lookup(16#a7, 16#4) -> {more, 16#af, 16#18}; -dec_huffman_lookup(16#a7, 16#5) -> {more, 16#af, 16#1f}; -dec_huffman_lookup(16#a7, 16#6) -> {more, 16#af, 16#29}; -dec_huffman_lookup(16#a7, 16#7) -> {ok, 16#af, 16#38}; -dec_huffman_lookup(16#a7, 16#8) -> {more, 16#b4, 16#03}; -dec_huffman_lookup(16#a7, 16#9) -> {more, 16#b4, 16#06}; -dec_huffman_lookup(16#a7, 16#a) -> {more, 16#b4, 16#0a}; -dec_huffman_lookup(16#a7, 16#b) -> {more, 16#b4, 16#0f}; -dec_huffman_lookup(16#a7, 16#c) -> {more, 16#b4, 16#18}; -dec_huffman_lookup(16#a7, 16#d) -> {more, 16#b4, 16#1f}; -dec_huffman_lookup(16#a7, 16#e) -> {more, 16#b4, 16#29}; -dec_huffman_lookup(16#a7, 16#f) -> {ok, 16#b4, 16#38}; -dec_huffman_lookup(16#a8, 16#0) -> {more, 16#b6, 16#03}; -dec_huffman_lookup(16#a8, 16#1) -> {more, 16#b6, 16#06}; -dec_huffman_lookup(16#a8, 16#2) -> {more, 16#b6, 16#0a}; -dec_huffman_lookup(16#a8, 16#3) -> {more, 16#b6, 16#0f}; -dec_huffman_lookup(16#a8, 16#4) -> {more, 16#b6, 16#18}; -dec_huffman_lookup(16#a8, 16#5) -> {more, 16#b6, 16#1f}; -dec_huffman_lookup(16#a8, 16#6) -> {more, 16#b6, 16#29}; -dec_huffman_lookup(16#a8, 16#7) -> {ok, 16#b6, 16#38}; -dec_huffman_lookup(16#a8, 16#8) -> {more, 16#b7, 16#03}; -dec_huffman_lookup(16#a8, 16#9) -> {more, 16#b7, 16#06}; -dec_huffman_lookup(16#a8, 16#a) -> {more, 16#b7, 16#0a}; -dec_huffman_lookup(16#a8, 16#b) -> {more, 16#b7, 16#0f}; -dec_huffman_lookup(16#a8, 16#c) -> {more, 16#b7, 16#18}; -dec_huffman_lookup(16#a8, 16#d) -> {more, 16#b7, 16#1f}; -dec_huffman_lookup(16#a8, 16#e) -> {more, 16#b7, 16#29}; -dec_huffman_lookup(16#a8, 16#f) -> {ok, 16#b7, 16#38}; -dec_huffman_lookup(16#a9, 16#0) -> {ok, 16#bc, 16#00}; -dec_huffman_lookup(16#a9, 16#1) -> {ok, 16#bf, 16#00}; -dec_huffman_lookup(16#a9, 16#2) -> {ok, 16#c5, 16#00}; -dec_huffman_lookup(16#a9, 16#3) -> {ok, 16#e7, 16#00}; -dec_huffman_lookup(16#a9, 16#4) -> {ok, 16#ef, 16#00}; -dec_huffman_lookup(16#a9, 16#5) -> {more, undefined, 16#b0}; -dec_huffman_lookup(16#a9, 16#6) -> {more, undefined, 16#b2}; -dec_huffman_lookup(16#a9, 16#7) -> {more, undefined, 16#b3}; -dec_huffman_lookup(16#a9, 16#8) -> {more, undefined, 16#b7}; -dec_huffman_lookup(16#a9, 16#9) -> {more, undefined, 16#b8}; -dec_huffman_lookup(16#a9, 16#a) -> {more, undefined, 16#ba}; -dec_huffman_lookup(16#a9, 16#b) -> {more, undefined, 16#bb}; -dec_huffman_lookup(16#a9, 16#c) -> {more, undefined, 16#c0}; -dec_huffman_lookup(16#a9, 16#d) -> {more, undefined, 16#c7}; -dec_huffman_lookup(16#a9, 16#e) -> {more, undefined, 16#d0}; -dec_huffman_lookup(16#a9, 16#f) -> {ok, undefined, 16#df}; -dec_huffman_lookup(16#aa, 16#0) -> {more, 16#bc, 16#01}; -dec_huffman_lookup(16#aa, 16#1) -> {ok, 16#bc, 16#16}; -dec_huffman_lookup(16#aa, 16#2) -> {more, 16#bf, 16#01}; -dec_huffman_lookup(16#aa, 16#3) -> {ok, 16#bf, 16#16}; -dec_huffman_lookup(16#aa, 16#4) -> {more, 16#c5, 16#01}; -dec_huffman_lookup(16#aa, 16#5) -> {ok, 16#c5, 16#16}; -dec_huffman_lookup(16#aa, 16#6) -> {more, 16#e7, 16#01}; -dec_huffman_lookup(16#aa, 16#7) -> {ok, 16#e7, 16#16}; -dec_huffman_lookup(16#aa, 16#8) -> {more, 16#ef, 16#01}; -dec_huffman_lookup(16#aa, 16#9) -> {ok, 16#ef, 16#16}; -dec_huffman_lookup(16#aa, 16#a) -> {ok, 16#09, 16#00}; -dec_huffman_lookup(16#aa, 16#b) -> {ok, 16#8e, 16#00}; -dec_huffman_lookup(16#aa, 16#c) -> {ok, 16#90, 16#00}; -dec_huffman_lookup(16#aa, 16#d) -> {ok, 16#91, 16#00}; -dec_huffman_lookup(16#aa, 16#e) -> {ok, 16#94, 16#00}; -dec_huffman_lookup(16#aa, 16#f) -> {ok, 16#9f, 16#00}; -dec_huffman_lookup(16#ab, 16#0) -> {more, 16#bc, 16#02}; -dec_huffman_lookup(16#ab, 16#1) -> {more, 16#bc, 16#09}; -dec_huffman_lookup(16#ab, 16#2) -> {more, 16#bc, 16#17}; -dec_huffman_lookup(16#ab, 16#3) -> {ok, 16#bc, 16#28}; -dec_huffman_lookup(16#ab, 16#4) -> {more, 16#bf, 16#02}; -dec_huffman_lookup(16#ab, 16#5) -> {more, 16#bf, 16#09}; -dec_huffman_lookup(16#ab, 16#6) -> {more, 16#bf, 16#17}; -dec_huffman_lookup(16#ab, 16#7) -> {ok, 16#bf, 16#28}; -dec_huffman_lookup(16#ab, 16#8) -> {more, 16#c5, 16#02}; -dec_huffman_lookup(16#ab, 16#9) -> {more, 16#c5, 16#09}; -dec_huffman_lookup(16#ab, 16#a) -> {more, 16#c5, 16#17}; -dec_huffman_lookup(16#ab, 16#b) -> {ok, 16#c5, 16#28}; -dec_huffman_lookup(16#ab, 16#c) -> {more, 16#e7, 16#02}; -dec_huffman_lookup(16#ab, 16#d) -> {more, 16#e7, 16#09}; -dec_huffman_lookup(16#ab, 16#e) -> {more, 16#e7, 16#17}; -dec_huffman_lookup(16#ab, 16#f) -> {ok, 16#e7, 16#28}; -dec_huffman_lookup(16#ac, 16#0) -> {more, 16#bc, 16#03}; -dec_huffman_lookup(16#ac, 16#1) -> {more, 16#bc, 16#06}; -dec_huffman_lookup(16#ac, 16#2) -> {more, 16#bc, 16#0a}; -dec_huffman_lookup(16#ac, 16#3) -> {more, 16#bc, 16#0f}; -dec_huffman_lookup(16#ac, 16#4) -> {more, 16#bc, 16#18}; -dec_huffman_lookup(16#ac, 16#5) -> {more, 16#bc, 16#1f}; -dec_huffman_lookup(16#ac, 16#6) -> {more, 16#bc, 16#29}; -dec_huffman_lookup(16#ac, 16#7) -> {ok, 16#bc, 16#38}; -dec_huffman_lookup(16#ac, 16#8) -> {more, 16#bf, 16#03}; -dec_huffman_lookup(16#ac, 16#9) -> {more, 16#bf, 16#06}; -dec_huffman_lookup(16#ac, 16#a) -> {more, 16#bf, 16#0a}; -dec_huffman_lookup(16#ac, 16#b) -> {more, 16#bf, 16#0f}; -dec_huffman_lookup(16#ac, 16#c) -> {more, 16#bf, 16#18}; -dec_huffman_lookup(16#ac, 16#d) -> {more, 16#bf, 16#1f}; -dec_huffman_lookup(16#ac, 16#e) -> {more, 16#bf, 16#29}; -dec_huffman_lookup(16#ac, 16#f) -> {ok, 16#bf, 16#38}; -dec_huffman_lookup(16#ad, 16#0) -> {more, 16#c5, 16#03}; -dec_huffman_lookup(16#ad, 16#1) -> {more, 16#c5, 16#06}; -dec_huffman_lookup(16#ad, 16#2) -> {more, 16#c5, 16#0a}; -dec_huffman_lookup(16#ad, 16#3) -> {more, 16#c5, 16#0f}; -dec_huffman_lookup(16#ad, 16#4) -> {more, 16#c5, 16#18}; -dec_huffman_lookup(16#ad, 16#5) -> {more, 16#c5, 16#1f}; -dec_huffman_lookup(16#ad, 16#6) -> {more, 16#c5, 16#29}; -dec_huffman_lookup(16#ad, 16#7) -> {ok, 16#c5, 16#38}; -dec_huffman_lookup(16#ad, 16#8) -> {more, 16#e7, 16#03}; -dec_huffman_lookup(16#ad, 16#9) -> {more, 16#e7, 16#06}; -dec_huffman_lookup(16#ad, 16#a) -> {more, 16#e7, 16#0a}; -dec_huffman_lookup(16#ad, 16#b) -> {more, 16#e7, 16#0f}; -dec_huffman_lookup(16#ad, 16#c) -> {more, 16#e7, 16#18}; -dec_huffman_lookup(16#ad, 16#d) -> {more, 16#e7, 16#1f}; -dec_huffman_lookup(16#ad, 16#e) -> {more, 16#e7, 16#29}; -dec_huffman_lookup(16#ad, 16#f) -> {ok, 16#e7, 16#38}; -dec_huffman_lookup(16#ae, 16#0) -> {more, 16#ef, 16#02}; -dec_huffman_lookup(16#ae, 16#1) -> {more, 16#ef, 16#09}; -dec_huffman_lookup(16#ae, 16#2) -> {more, 16#ef, 16#17}; -dec_huffman_lookup(16#ae, 16#3) -> {ok, 16#ef, 16#28}; -dec_huffman_lookup(16#ae, 16#4) -> {more, 16#09, 16#01}; -dec_huffman_lookup(16#ae, 16#5) -> {ok, 16#09, 16#16}; -dec_huffman_lookup(16#ae, 16#6) -> {more, 16#8e, 16#01}; -dec_huffman_lookup(16#ae, 16#7) -> {ok, 16#8e, 16#16}; -dec_huffman_lookup(16#ae, 16#8) -> {more, 16#90, 16#01}; -dec_huffman_lookup(16#ae, 16#9) -> {ok, 16#90, 16#16}; -dec_huffman_lookup(16#ae, 16#a) -> {more, 16#91, 16#01}; -dec_huffman_lookup(16#ae, 16#b) -> {ok, 16#91, 16#16}; -dec_huffman_lookup(16#ae, 16#c) -> {more, 16#94, 16#01}; -dec_huffman_lookup(16#ae, 16#d) -> {ok, 16#94, 16#16}; -dec_huffman_lookup(16#ae, 16#e) -> {more, 16#9f, 16#01}; -dec_huffman_lookup(16#ae, 16#f) -> {ok, 16#9f, 16#16}; -dec_huffman_lookup(16#af, 16#0) -> {more, 16#ef, 16#03}; -dec_huffman_lookup(16#af, 16#1) -> {more, 16#ef, 16#06}; -dec_huffman_lookup(16#af, 16#2) -> {more, 16#ef, 16#0a}; -dec_huffman_lookup(16#af, 16#3) -> {more, 16#ef, 16#0f}; -dec_huffman_lookup(16#af, 16#4) -> {more, 16#ef, 16#18}; -dec_huffman_lookup(16#af, 16#5) -> {more, 16#ef, 16#1f}; -dec_huffman_lookup(16#af, 16#6) -> {more, 16#ef, 16#29}; -dec_huffman_lookup(16#af, 16#7) -> {ok, 16#ef, 16#38}; -dec_huffman_lookup(16#af, 16#8) -> {more, 16#09, 16#02}; -dec_huffman_lookup(16#af, 16#9) -> {more, 16#09, 16#09}; -dec_huffman_lookup(16#af, 16#a) -> {more, 16#09, 16#17}; -dec_huffman_lookup(16#af, 16#b) -> {ok, 16#09, 16#28}; -dec_huffman_lookup(16#af, 16#c) -> {more, 16#8e, 16#02}; -dec_huffman_lookup(16#af, 16#d) -> {more, 16#8e, 16#09}; -dec_huffman_lookup(16#af, 16#e) -> {more, 16#8e, 16#17}; -dec_huffman_lookup(16#af, 16#f) -> {ok, 16#8e, 16#28}; -dec_huffman_lookup(16#b0, 16#0) -> {more, 16#09, 16#03}; -dec_huffman_lookup(16#b0, 16#1) -> {more, 16#09, 16#06}; -dec_huffman_lookup(16#b0, 16#2) -> {more, 16#09, 16#0a}; -dec_huffman_lookup(16#b0, 16#3) -> {more, 16#09, 16#0f}; -dec_huffman_lookup(16#b0, 16#4) -> {more, 16#09, 16#18}; -dec_huffman_lookup(16#b0, 16#5) -> {more, 16#09, 16#1f}; -dec_huffman_lookup(16#b0, 16#6) -> {more, 16#09, 16#29}; -dec_huffman_lookup(16#b0, 16#7) -> {ok, 16#09, 16#38}; -dec_huffman_lookup(16#b0, 16#8) -> {more, 16#8e, 16#03}; -dec_huffman_lookup(16#b0, 16#9) -> {more, 16#8e, 16#06}; -dec_huffman_lookup(16#b0, 16#a) -> {more, 16#8e, 16#0a}; -dec_huffman_lookup(16#b0, 16#b) -> {more, 16#8e, 16#0f}; -dec_huffman_lookup(16#b0, 16#c) -> {more, 16#8e, 16#18}; -dec_huffman_lookup(16#b0, 16#d) -> {more, 16#8e, 16#1f}; -dec_huffman_lookup(16#b0, 16#e) -> {more, 16#8e, 16#29}; -dec_huffman_lookup(16#b0, 16#f) -> {ok, 16#8e, 16#38}; -dec_huffman_lookup(16#b1, 16#0) -> {more, 16#90, 16#02}; -dec_huffman_lookup(16#b1, 16#1) -> {more, 16#90, 16#09}; -dec_huffman_lookup(16#b1, 16#2) -> {more, 16#90, 16#17}; -dec_huffman_lookup(16#b1, 16#3) -> {ok, 16#90, 16#28}; -dec_huffman_lookup(16#b1, 16#4) -> {more, 16#91, 16#02}; -dec_huffman_lookup(16#b1, 16#5) -> {more, 16#91, 16#09}; -dec_huffman_lookup(16#b1, 16#6) -> {more, 16#91, 16#17}; -dec_huffman_lookup(16#b1, 16#7) -> {ok, 16#91, 16#28}; -dec_huffman_lookup(16#b1, 16#8) -> {more, 16#94, 16#02}; -dec_huffman_lookup(16#b1, 16#9) -> {more, 16#94, 16#09}; -dec_huffman_lookup(16#b1, 16#a) -> {more, 16#94, 16#17}; -dec_huffman_lookup(16#b1, 16#b) -> {ok, 16#94, 16#28}; -dec_huffman_lookup(16#b1, 16#c) -> {more, 16#9f, 16#02}; -dec_huffman_lookup(16#b1, 16#d) -> {more, 16#9f, 16#09}; -dec_huffman_lookup(16#b1, 16#e) -> {more, 16#9f, 16#17}; -dec_huffman_lookup(16#b1, 16#f) -> {ok, 16#9f, 16#28}; -dec_huffman_lookup(16#b2, 16#0) -> {more, 16#90, 16#03}; -dec_huffman_lookup(16#b2, 16#1) -> {more, 16#90, 16#06}; -dec_huffman_lookup(16#b2, 16#2) -> {more, 16#90, 16#0a}; -dec_huffman_lookup(16#b2, 16#3) -> {more, 16#90, 16#0f}; -dec_huffman_lookup(16#b2, 16#4) -> {more, 16#90, 16#18}; -dec_huffman_lookup(16#b2, 16#5) -> {more, 16#90, 16#1f}; -dec_huffman_lookup(16#b2, 16#6) -> {more, 16#90, 16#29}; -dec_huffman_lookup(16#b2, 16#7) -> {ok, 16#90, 16#38}; -dec_huffman_lookup(16#b2, 16#8) -> {more, 16#91, 16#03}; -dec_huffman_lookup(16#b2, 16#9) -> {more, 16#91, 16#06}; -dec_huffman_lookup(16#b2, 16#a) -> {more, 16#91, 16#0a}; -dec_huffman_lookup(16#b2, 16#b) -> {more, 16#91, 16#0f}; -dec_huffman_lookup(16#b2, 16#c) -> {more, 16#91, 16#18}; -dec_huffman_lookup(16#b2, 16#d) -> {more, 16#91, 16#1f}; -dec_huffman_lookup(16#b2, 16#e) -> {more, 16#91, 16#29}; -dec_huffman_lookup(16#b2, 16#f) -> {ok, 16#91, 16#38}; -dec_huffman_lookup(16#b3, 16#0) -> {more, 16#94, 16#03}; -dec_huffman_lookup(16#b3, 16#1) -> {more, 16#94, 16#06}; -dec_huffman_lookup(16#b3, 16#2) -> {more, 16#94, 16#0a}; -dec_huffman_lookup(16#b3, 16#3) -> {more, 16#94, 16#0f}; -dec_huffman_lookup(16#b3, 16#4) -> {more, 16#94, 16#18}; -dec_huffman_lookup(16#b3, 16#5) -> {more, 16#94, 16#1f}; -dec_huffman_lookup(16#b3, 16#6) -> {more, 16#94, 16#29}; -dec_huffman_lookup(16#b3, 16#7) -> {ok, 16#94, 16#38}; -dec_huffman_lookup(16#b3, 16#8) -> {more, 16#9f, 16#03}; -dec_huffman_lookup(16#b3, 16#9) -> {more, 16#9f, 16#06}; -dec_huffman_lookup(16#b3, 16#a) -> {more, 16#9f, 16#0a}; -dec_huffman_lookup(16#b3, 16#b) -> {more, 16#9f, 16#0f}; -dec_huffman_lookup(16#b3, 16#c) -> {more, 16#9f, 16#18}; -dec_huffman_lookup(16#b3, 16#d) -> {more, 16#9f, 16#1f}; -dec_huffman_lookup(16#b3, 16#e) -> {more, 16#9f, 16#29}; -dec_huffman_lookup(16#b3, 16#f) -> {ok, 16#9f, 16#38}; -dec_huffman_lookup(16#b4, 16#0) -> {ok, 16#ab, 16#00}; -dec_huffman_lookup(16#b4, 16#1) -> {ok, 16#ce, 16#00}; -dec_huffman_lookup(16#b4, 16#2) -> {ok, 16#d7, 16#00}; -dec_huffman_lookup(16#b4, 16#3) -> {ok, 16#e1, 16#00}; -dec_huffman_lookup(16#b4, 16#4) -> {ok, 16#ec, 16#00}; -dec_huffman_lookup(16#b4, 16#5) -> {ok, 16#ed, 16#00}; -dec_huffman_lookup(16#b4, 16#6) -> {more, undefined, 16#bc}; -dec_huffman_lookup(16#b4, 16#7) -> {more, undefined, 16#bd}; -dec_huffman_lookup(16#b4, 16#8) -> {more, undefined, 16#c1}; -dec_huffman_lookup(16#b4, 16#9) -> {more, undefined, 16#c4}; -dec_huffman_lookup(16#b4, 16#a) -> {more, undefined, 16#c8}; -dec_huffman_lookup(16#b4, 16#b) -> {more, undefined, 16#cb}; -dec_huffman_lookup(16#b4, 16#c) -> {more, undefined, 16#d1}; -dec_huffman_lookup(16#b4, 16#d) -> {more, undefined, 16#d8}; -dec_huffman_lookup(16#b4, 16#e) -> {more, undefined, 16#e0}; -dec_huffman_lookup(16#b4, 16#f) -> {ok, undefined, 16#ee}; -dec_huffman_lookup(16#b5, 16#0) -> {more, 16#ab, 16#01}; -dec_huffman_lookup(16#b5, 16#1) -> {ok, 16#ab, 16#16}; -dec_huffman_lookup(16#b5, 16#2) -> {more, 16#ce, 16#01}; -dec_huffman_lookup(16#b5, 16#3) -> {ok, 16#ce, 16#16}; -dec_huffman_lookup(16#b5, 16#4) -> {more, 16#d7, 16#01}; -dec_huffman_lookup(16#b5, 16#5) -> {ok, 16#d7, 16#16}; -dec_huffman_lookup(16#b5, 16#6) -> {more, 16#e1, 16#01}; -dec_huffman_lookup(16#b5, 16#7) -> {ok, 16#e1, 16#16}; -dec_huffman_lookup(16#b5, 16#8) -> {more, 16#ec, 16#01}; -dec_huffman_lookup(16#b5, 16#9) -> {ok, 16#ec, 16#16}; -dec_huffman_lookup(16#b5, 16#a) -> {more, 16#ed, 16#01}; -dec_huffman_lookup(16#b5, 16#b) -> {ok, 16#ed, 16#16}; -dec_huffman_lookup(16#b5, 16#c) -> {ok, 16#c7, 16#00}; -dec_huffman_lookup(16#b5, 16#d) -> {ok, 16#cf, 16#00}; -dec_huffman_lookup(16#b5, 16#e) -> {ok, 16#ea, 16#00}; -dec_huffman_lookup(16#b5, 16#f) -> {ok, 16#eb, 16#00}; -dec_huffman_lookup(16#b6, 16#0) -> {more, 16#ab, 16#02}; -dec_huffman_lookup(16#b6, 16#1) -> {more, 16#ab, 16#09}; -dec_huffman_lookup(16#b6, 16#2) -> {more, 16#ab, 16#17}; -dec_huffman_lookup(16#b6, 16#3) -> {ok, 16#ab, 16#28}; -dec_huffman_lookup(16#b6, 16#4) -> {more, 16#ce, 16#02}; -dec_huffman_lookup(16#b6, 16#5) -> {more, 16#ce, 16#09}; -dec_huffman_lookup(16#b6, 16#6) -> {more, 16#ce, 16#17}; -dec_huffman_lookup(16#b6, 16#7) -> {ok, 16#ce, 16#28}; -dec_huffman_lookup(16#b6, 16#8) -> {more, 16#d7, 16#02}; -dec_huffman_lookup(16#b6, 16#9) -> {more, 16#d7, 16#09}; -dec_huffman_lookup(16#b6, 16#a) -> {more, 16#d7, 16#17}; -dec_huffman_lookup(16#b6, 16#b) -> {ok, 16#d7, 16#28}; -dec_huffman_lookup(16#b6, 16#c) -> {more, 16#e1, 16#02}; -dec_huffman_lookup(16#b6, 16#d) -> {more, 16#e1, 16#09}; -dec_huffman_lookup(16#b6, 16#e) -> {more, 16#e1, 16#17}; -dec_huffman_lookup(16#b6, 16#f) -> {ok, 16#e1, 16#28}; -dec_huffman_lookup(16#b7, 16#0) -> {more, 16#ab, 16#03}; -dec_huffman_lookup(16#b7, 16#1) -> {more, 16#ab, 16#06}; -dec_huffman_lookup(16#b7, 16#2) -> {more, 16#ab, 16#0a}; -dec_huffman_lookup(16#b7, 16#3) -> {more, 16#ab, 16#0f}; -dec_huffman_lookup(16#b7, 16#4) -> {more, 16#ab, 16#18}; -dec_huffman_lookup(16#b7, 16#5) -> {more, 16#ab, 16#1f}; -dec_huffman_lookup(16#b7, 16#6) -> {more, 16#ab, 16#29}; -dec_huffman_lookup(16#b7, 16#7) -> {ok, 16#ab, 16#38}; -dec_huffman_lookup(16#b7, 16#8) -> {more, 16#ce, 16#03}; -dec_huffman_lookup(16#b7, 16#9) -> {more, 16#ce, 16#06}; -dec_huffman_lookup(16#b7, 16#a) -> {more, 16#ce, 16#0a}; -dec_huffman_lookup(16#b7, 16#b) -> {more, 16#ce, 16#0f}; -dec_huffman_lookup(16#b7, 16#c) -> {more, 16#ce, 16#18}; -dec_huffman_lookup(16#b7, 16#d) -> {more, 16#ce, 16#1f}; -dec_huffman_lookup(16#b7, 16#e) -> {more, 16#ce, 16#29}; -dec_huffman_lookup(16#b7, 16#f) -> {ok, 16#ce, 16#38}; -dec_huffman_lookup(16#b8, 16#0) -> {more, 16#d7, 16#03}; -dec_huffman_lookup(16#b8, 16#1) -> {more, 16#d7, 16#06}; -dec_huffman_lookup(16#b8, 16#2) -> {more, 16#d7, 16#0a}; -dec_huffman_lookup(16#b8, 16#3) -> {more, 16#d7, 16#0f}; -dec_huffman_lookup(16#b8, 16#4) -> {more, 16#d7, 16#18}; -dec_huffman_lookup(16#b8, 16#5) -> {more, 16#d7, 16#1f}; -dec_huffman_lookup(16#b8, 16#6) -> {more, 16#d7, 16#29}; -dec_huffman_lookup(16#b8, 16#7) -> {ok, 16#d7, 16#38}; -dec_huffman_lookup(16#b8, 16#8) -> {more, 16#e1, 16#03}; -dec_huffman_lookup(16#b8, 16#9) -> {more, 16#e1, 16#06}; -dec_huffman_lookup(16#b8, 16#a) -> {more, 16#e1, 16#0a}; -dec_huffman_lookup(16#b8, 16#b) -> {more, 16#e1, 16#0f}; -dec_huffman_lookup(16#b8, 16#c) -> {more, 16#e1, 16#18}; -dec_huffman_lookup(16#b8, 16#d) -> {more, 16#e1, 16#1f}; -dec_huffman_lookup(16#b8, 16#e) -> {more, 16#e1, 16#29}; -dec_huffman_lookup(16#b8, 16#f) -> {ok, 16#e1, 16#38}; -dec_huffman_lookup(16#b9, 16#0) -> {more, 16#ec, 16#02}; -dec_huffman_lookup(16#b9, 16#1) -> {more, 16#ec, 16#09}; -dec_huffman_lookup(16#b9, 16#2) -> {more, 16#ec, 16#17}; -dec_huffman_lookup(16#b9, 16#3) -> {ok, 16#ec, 16#28}; -dec_huffman_lookup(16#b9, 16#4) -> {more, 16#ed, 16#02}; -dec_huffman_lookup(16#b9, 16#5) -> {more, 16#ed, 16#09}; -dec_huffman_lookup(16#b9, 16#6) -> {more, 16#ed, 16#17}; -dec_huffman_lookup(16#b9, 16#7) -> {ok, 16#ed, 16#28}; -dec_huffman_lookup(16#b9, 16#8) -> {more, 16#c7, 16#01}; -dec_huffman_lookup(16#b9, 16#9) -> {ok, 16#c7, 16#16}; -dec_huffman_lookup(16#b9, 16#a) -> {more, 16#cf, 16#01}; -dec_huffman_lookup(16#b9, 16#b) -> {ok, 16#cf, 16#16}; -dec_huffman_lookup(16#b9, 16#c) -> {more, 16#ea, 16#01}; -dec_huffman_lookup(16#b9, 16#d) -> {ok, 16#ea, 16#16}; -dec_huffman_lookup(16#b9, 16#e) -> {more, 16#eb, 16#01}; -dec_huffman_lookup(16#b9, 16#f) -> {ok, 16#eb, 16#16}; -dec_huffman_lookup(16#ba, 16#0) -> {more, 16#ec, 16#03}; -dec_huffman_lookup(16#ba, 16#1) -> {more, 16#ec, 16#06}; -dec_huffman_lookup(16#ba, 16#2) -> {more, 16#ec, 16#0a}; -dec_huffman_lookup(16#ba, 16#3) -> {more, 16#ec, 16#0f}; -dec_huffman_lookup(16#ba, 16#4) -> {more, 16#ec, 16#18}; -dec_huffman_lookup(16#ba, 16#5) -> {more, 16#ec, 16#1f}; -dec_huffman_lookup(16#ba, 16#6) -> {more, 16#ec, 16#29}; -dec_huffman_lookup(16#ba, 16#7) -> {ok, 16#ec, 16#38}; -dec_huffman_lookup(16#ba, 16#8) -> {more, 16#ed, 16#03}; -dec_huffman_lookup(16#ba, 16#9) -> {more, 16#ed, 16#06}; -dec_huffman_lookup(16#ba, 16#a) -> {more, 16#ed, 16#0a}; -dec_huffman_lookup(16#ba, 16#b) -> {more, 16#ed, 16#0f}; -dec_huffman_lookup(16#ba, 16#c) -> {more, 16#ed, 16#18}; -dec_huffman_lookup(16#ba, 16#d) -> {more, 16#ed, 16#1f}; -dec_huffman_lookup(16#ba, 16#e) -> {more, 16#ed, 16#29}; -dec_huffman_lookup(16#ba, 16#f) -> {ok, 16#ed, 16#38}; -dec_huffman_lookup(16#bb, 16#0) -> {more, 16#c7, 16#02}; -dec_huffman_lookup(16#bb, 16#1) -> {more, 16#c7, 16#09}; -dec_huffman_lookup(16#bb, 16#2) -> {more, 16#c7, 16#17}; -dec_huffman_lookup(16#bb, 16#3) -> {ok, 16#c7, 16#28}; -dec_huffman_lookup(16#bb, 16#4) -> {more, 16#cf, 16#02}; -dec_huffman_lookup(16#bb, 16#5) -> {more, 16#cf, 16#09}; -dec_huffman_lookup(16#bb, 16#6) -> {more, 16#cf, 16#17}; -dec_huffman_lookup(16#bb, 16#7) -> {ok, 16#cf, 16#28}; -dec_huffman_lookup(16#bb, 16#8) -> {more, 16#ea, 16#02}; -dec_huffman_lookup(16#bb, 16#9) -> {more, 16#ea, 16#09}; -dec_huffman_lookup(16#bb, 16#a) -> {more, 16#ea, 16#17}; -dec_huffman_lookup(16#bb, 16#b) -> {ok, 16#ea, 16#28}; -dec_huffman_lookup(16#bb, 16#c) -> {more, 16#eb, 16#02}; -dec_huffman_lookup(16#bb, 16#d) -> {more, 16#eb, 16#09}; -dec_huffman_lookup(16#bb, 16#e) -> {more, 16#eb, 16#17}; -dec_huffman_lookup(16#bb, 16#f) -> {ok, 16#eb, 16#28}; -dec_huffman_lookup(16#bc, 16#0) -> {more, 16#c7, 16#03}; -dec_huffman_lookup(16#bc, 16#1) -> {more, 16#c7, 16#06}; -dec_huffman_lookup(16#bc, 16#2) -> {more, 16#c7, 16#0a}; -dec_huffman_lookup(16#bc, 16#3) -> {more, 16#c7, 16#0f}; -dec_huffman_lookup(16#bc, 16#4) -> {more, 16#c7, 16#18}; -dec_huffman_lookup(16#bc, 16#5) -> {more, 16#c7, 16#1f}; -dec_huffman_lookup(16#bc, 16#6) -> {more, 16#c7, 16#29}; -dec_huffman_lookup(16#bc, 16#7) -> {ok, 16#c7, 16#38}; -dec_huffman_lookup(16#bc, 16#8) -> {more, 16#cf, 16#03}; -dec_huffman_lookup(16#bc, 16#9) -> {more, 16#cf, 16#06}; -dec_huffman_lookup(16#bc, 16#a) -> {more, 16#cf, 16#0a}; -dec_huffman_lookup(16#bc, 16#b) -> {more, 16#cf, 16#0f}; -dec_huffman_lookup(16#bc, 16#c) -> {more, 16#cf, 16#18}; -dec_huffman_lookup(16#bc, 16#d) -> {more, 16#cf, 16#1f}; -dec_huffman_lookup(16#bc, 16#e) -> {more, 16#cf, 16#29}; -dec_huffman_lookup(16#bc, 16#f) -> {ok, 16#cf, 16#38}; -dec_huffman_lookup(16#bd, 16#0) -> {more, 16#ea, 16#03}; -dec_huffman_lookup(16#bd, 16#1) -> {more, 16#ea, 16#06}; -dec_huffman_lookup(16#bd, 16#2) -> {more, 16#ea, 16#0a}; -dec_huffman_lookup(16#bd, 16#3) -> {more, 16#ea, 16#0f}; -dec_huffman_lookup(16#bd, 16#4) -> {more, 16#ea, 16#18}; -dec_huffman_lookup(16#bd, 16#5) -> {more, 16#ea, 16#1f}; -dec_huffman_lookup(16#bd, 16#6) -> {more, 16#ea, 16#29}; -dec_huffman_lookup(16#bd, 16#7) -> {ok, 16#ea, 16#38}; -dec_huffman_lookup(16#bd, 16#8) -> {more, 16#eb, 16#03}; -dec_huffman_lookup(16#bd, 16#9) -> {more, 16#eb, 16#06}; -dec_huffman_lookup(16#bd, 16#a) -> {more, 16#eb, 16#0a}; -dec_huffman_lookup(16#bd, 16#b) -> {more, 16#eb, 16#0f}; -dec_huffman_lookup(16#bd, 16#c) -> {more, 16#eb, 16#18}; -dec_huffman_lookup(16#bd, 16#d) -> {more, 16#eb, 16#1f}; -dec_huffman_lookup(16#bd, 16#e) -> {more, 16#eb, 16#29}; -dec_huffman_lookup(16#bd, 16#f) -> {ok, 16#eb, 16#38}; -dec_huffman_lookup(16#be, 16#0) -> {more, undefined, 16#c2}; -dec_huffman_lookup(16#be, 16#1) -> {more, undefined, 16#c3}; -dec_huffman_lookup(16#be, 16#2) -> {more, undefined, 16#c5}; -dec_huffman_lookup(16#be, 16#3) -> {more, undefined, 16#c6}; -dec_huffman_lookup(16#be, 16#4) -> {more, undefined, 16#c9}; -dec_huffman_lookup(16#be, 16#5) -> {more, undefined, 16#ca}; -dec_huffman_lookup(16#be, 16#6) -> {more, undefined, 16#cc}; -dec_huffman_lookup(16#be, 16#7) -> {more, undefined, 16#cd}; -dec_huffman_lookup(16#be, 16#8) -> {more, undefined, 16#d2}; -dec_huffman_lookup(16#be, 16#9) -> {more, undefined, 16#d5}; -dec_huffman_lookup(16#be, 16#a) -> {more, undefined, 16#d9}; -dec_huffman_lookup(16#be, 16#b) -> {more, undefined, 16#dc}; -dec_huffman_lookup(16#be, 16#c) -> {more, undefined, 16#e1}; -dec_huffman_lookup(16#be, 16#d) -> {more, undefined, 16#e7}; -dec_huffman_lookup(16#be, 16#e) -> {more, undefined, 16#ef}; -dec_huffman_lookup(16#be, 16#f) -> {ok, undefined, 16#f6}; -dec_huffman_lookup(16#bf, 16#0) -> {ok, 16#c0, 16#00}; -dec_huffman_lookup(16#bf, 16#1) -> {ok, 16#c1, 16#00}; -dec_huffman_lookup(16#bf, 16#2) -> {ok, 16#c8, 16#00}; -dec_huffman_lookup(16#bf, 16#3) -> {ok, 16#c9, 16#00}; -dec_huffman_lookup(16#bf, 16#4) -> {ok, 16#ca, 16#00}; -dec_huffman_lookup(16#bf, 16#5) -> {ok, 16#cd, 16#00}; -dec_huffman_lookup(16#bf, 16#6) -> {ok, 16#d2, 16#00}; -dec_huffman_lookup(16#bf, 16#7) -> {ok, 16#d5, 16#00}; -dec_huffman_lookup(16#bf, 16#8) -> {ok, 16#da, 16#00}; -dec_huffman_lookup(16#bf, 16#9) -> {ok, 16#db, 16#00}; -dec_huffman_lookup(16#bf, 16#a) -> {ok, 16#ee, 16#00}; -dec_huffman_lookup(16#bf, 16#b) -> {ok, 16#f0, 16#00}; -dec_huffman_lookup(16#bf, 16#c) -> {ok, 16#f2, 16#00}; -dec_huffman_lookup(16#bf, 16#d) -> {ok, 16#f3, 16#00}; -dec_huffman_lookup(16#bf, 16#e) -> {ok, 16#ff, 16#00}; -dec_huffman_lookup(16#bf, 16#f) -> {more, undefined, 16#ce}; -dec_huffman_lookup(16#c0, 16#0) -> {more, 16#c0, 16#01}; -dec_huffman_lookup(16#c0, 16#1) -> {ok, 16#c0, 16#16}; -dec_huffman_lookup(16#c0, 16#2) -> {more, 16#c1, 16#01}; -dec_huffman_lookup(16#c0, 16#3) -> {ok, 16#c1, 16#16}; -dec_huffman_lookup(16#c0, 16#4) -> {more, 16#c8, 16#01}; -dec_huffman_lookup(16#c0, 16#5) -> {ok, 16#c8, 16#16}; -dec_huffman_lookup(16#c0, 16#6) -> {more, 16#c9, 16#01}; -dec_huffman_lookup(16#c0, 16#7) -> {ok, 16#c9, 16#16}; -dec_huffman_lookup(16#c0, 16#8) -> {more, 16#ca, 16#01}; -dec_huffman_lookup(16#c0, 16#9) -> {ok, 16#ca, 16#16}; -dec_huffman_lookup(16#c0, 16#a) -> {more, 16#cd, 16#01}; -dec_huffman_lookup(16#c0, 16#b) -> {ok, 16#cd, 16#16}; -dec_huffman_lookup(16#c0, 16#c) -> {more, 16#d2, 16#01}; -dec_huffman_lookup(16#c0, 16#d) -> {ok, 16#d2, 16#16}; -dec_huffman_lookup(16#c0, 16#e) -> {more, 16#d5, 16#01}; -dec_huffman_lookup(16#c0, 16#f) -> {ok, 16#d5, 16#16}; -dec_huffman_lookup(16#c1, 16#0) -> {more, 16#c0, 16#02}; -dec_huffman_lookup(16#c1, 16#1) -> {more, 16#c0, 16#09}; -dec_huffman_lookup(16#c1, 16#2) -> {more, 16#c0, 16#17}; -dec_huffman_lookup(16#c1, 16#3) -> {ok, 16#c0, 16#28}; -dec_huffman_lookup(16#c1, 16#4) -> {more, 16#c1, 16#02}; -dec_huffman_lookup(16#c1, 16#5) -> {more, 16#c1, 16#09}; -dec_huffman_lookup(16#c1, 16#6) -> {more, 16#c1, 16#17}; -dec_huffman_lookup(16#c1, 16#7) -> {ok, 16#c1, 16#28}; -dec_huffman_lookup(16#c1, 16#8) -> {more, 16#c8, 16#02}; -dec_huffman_lookup(16#c1, 16#9) -> {more, 16#c8, 16#09}; -dec_huffman_lookup(16#c1, 16#a) -> {more, 16#c8, 16#17}; -dec_huffman_lookup(16#c1, 16#b) -> {ok, 16#c8, 16#28}; -dec_huffman_lookup(16#c1, 16#c) -> {more, 16#c9, 16#02}; -dec_huffman_lookup(16#c1, 16#d) -> {more, 16#c9, 16#09}; -dec_huffman_lookup(16#c1, 16#e) -> {more, 16#c9, 16#17}; -dec_huffman_lookup(16#c1, 16#f) -> {ok, 16#c9, 16#28}; -dec_huffman_lookup(16#c2, 16#0) -> {more, 16#c0, 16#03}; -dec_huffman_lookup(16#c2, 16#1) -> {more, 16#c0, 16#06}; -dec_huffman_lookup(16#c2, 16#2) -> {more, 16#c0, 16#0a}; -dec_huffman_lookup(16#c2, 16#3) -> {more, 16#c0, 16#0f}; -dec_huffman_lookup(16#c2, 16#4) -> {more, 16#c0, 16#18}; -dec_huffman_lookup(16#c2, 16#5) -> {more, 16#c0, 16#1f}; -dec_huffman_lookup(16#c2, 16#6) -> {more, 16#c0, 16#29}; -dec_huffman_lookup(16#c2, 16#7) -> {ok, 16#c0, 16#38}; -dec_huffman_lookup(16#c2, 16#8) -> {more, 16#c1, 16#03}; -dec_huffman_lookup(16#c2, 16#9) -> {more, 16#c1, 16#06}; -dec_huffman_lookup(16#c2, 16#a) -> {more, 16#c1, 16#0a}; -dec_huffman_lookup(16#c2, 16#b) -> {more, 16#c1, 16#0f}; -dec_huffman_lookup(16#c2, 16#c) -> {more, 16#c1, 16#18}; -dec_huffman_lookup(16#c2, 16#d) -> {more, 16#c1, 16#1f}; -dec_huffman_lookup(16#c2, 16#e) -> {more, 16#c1, 16#29}; -dec_huffman_lookup(16#c2, 16#f) -> {ok, 16#c1, 16#38}; -dec_huffman_lookup(16#c3, 16#0) -> {more, 16#c8, 16#03}; -dec_huffman_lookup(16#c3, 16#1) -> {more, 16#c8, 16#06}; -dec_huffman_lookup(16#c3, 16#2) -> {more, 16#c8, 16#0a}; -dec_huffman_lookup(16#c3, 16#3) -> {more, 16#c8, 16#0f}; -dec_huffman_lookup(16#c3, 16#4) -> {more, 16#c8, 16#18}; -dec_huffman_lookup(16#c3, 16#5) -> {more, 16#c8, 16#1f}; -dec_huffman_lookup(16#c3, 16#6) -> {more, 16#c8, 16#29}; -dec_huffman_lookup(16#c3, 16#7) -> {ok, 16#c8, 16#38}; -dec_huffman_lookup(16#c3, 16#8) -> {more, 16#c9, 16#03}; -dec_huffman_lookup(16#c3, 16#9) -> {more, 16#c9, 16#06}; -dec_huffman_lookup(16#c3, 16#a) -> {more, 16#c9, 16#0a}; -dec_huffman_lookup(16#c3, 16#b) -> {more, 16#c9, 16#0f}; -dec_huffman_lookup(16#c3, 16#c) -> {more, 16#c9, 16#18}; -dec_huffman_lookup(16#c3, 16#d) -> {more, 16#c9, 16#1f}; -dec_huffman_lookup(16#c3, 16#e) -> {more, 16#c9, 16#29}; -dec_huffman_lookup(16#c3, 16#f) -> {ok, 16#c9, 16#38}; -dec_huffman_lookup(16#c4, 16#0) -> {more, 16#ca, 16#02}; -dec_huffman_lookup(16#c4, 16#1) -> {more, 16#ca, 16#09}; -dec_huffman_lookup(16#c4, 16#2) -> {more, 16#ca, 16#17}; -dec_huffman_lookup(16#c4, 16#3) -> {ok, 16#ca, 16#28}; -dec_huffman_lookup(16#c4, 16#4) -> {more, 16#cd, 16#02}; -dec_huffman_lookup(16#c4, 16#5) -> {more, 16#cd, 16#09}; -dec_huffman_lookup(16#c4, 16#6) -> {more, 16#cd, 16#17}; -dec_huffman_lookup(16#c4, 16#7) -> {ok, 16#cd, 16#28}; -dec_huffman_lookup(16#c4, 16#8) -> {more, 16#d2, 16#02}; -dec_huffman_lookup(16#c4, 16#9) -> {more, 16#d2, 16#09}; -dec_huffman_lookup(16#c4, 16#a) -> {more, 16#d2, 16#17}; -dec_huffman_lookup(16#c4, 16#b) -> {ok, 16#d2, 16#28}; -dec_huffman_lookup(16#c4, 16#c) -> {more, 16#d5, 16#02}; -dec_huffman_lookup(16#c4, 16#d) -> {more, 16#d5, 16#09}; -dec_huffman_lookup(16#c4, 16#e) -> {more, 16#d5, 16#17}; -dec_huffman_lookup(16#c4, 16#f) -> {ok, 16#d5, 16#28}; -dec_huffman_lookup(16#c5, 16#0) -> {more, 16#ca, 16#03}; -dec_huffman_lookup(16#c5, 16#1) -> {more, 16#ca, 16#06}; -dec_huffman_lookup(16#c5, 16#2) -> {more, 16#ca, 16#0a}; -dec_huffman_lookup(16#c5, 16#3) -> {more, 16#ca, 16#0f}; -dec_huffman_lookup(16#c5, 16#4) -> {more, 16#ca, 16#18}; -dec_huffman_lookup(16#c5, 16#5) -> {more, 16#ca, 16#1f}; -dec_huffman_lookup(16#c5, 16#6) -> {more, 16#ca, 16#29}; -dec_huffman_lookup(16#c5, 16#7) -> {ok, 16#ca, 16#38}; -dec_huffman_lookup(16#c5, 16#8) -> {more, 16#cd, 16#03}; -dec_huffman_lookup(16#c5, 16#9) -> {more, 16#cd, 16#06}; -dec_huffman_lookup(16#c5, 16#a) -> {more, 16#cd, 16#0a}; -dec_huffman_lookup(16#c5, 16#b) -> {more, 16#cd, 16#0f}; -dec_huffman_lookup(16#c5, 16#c) -> {more, 16#cd, 16#18}; -dec_huffman_lookup(16#c5, 16#d) -> {more, 16#cd, 16#1f}; -dec_huffman_lookup(16#c5, 16#e) -> {more, 16#cd, 16#29}; -dec_huffman_lookup(16#c5, 16#f) -> {ok, 16#cd, 16#38}; -dec_huffman_lookup(16#c6, 16#0) -> {more, 16#d2, 16#03}; -dec_huffman_lookup(16#c6, 16#1) -> {more, 16#d2, 16#06}; -dec_huffman_lookup(16#c6, 16#2) -> {more, 16#d2, 16#0a}; -dec_huffman_lookup(16#c6, 16#3) -> {more, 16#d2, 16#0f}; -dec_huffman_lookup(16#c6, 16#4) -> {more, 16#d2, 16#18}; -dec_huffman_lookup(16#c6, 16#5) -> {more, 16#d2, 16#1f}; -dec_huffman_lookup(16#c6, 16#6) -> {more, 16#d2, 16#29}; -dec_huffman_lookup(16#c6, 16#7) -> {ok, 16#d2, 16#38}; -dec_huffman_lookup(16#c6, 16#8) -> {more, 16#d5, 16#03}; -dec_huffman_lookup(16#c6, 16#9) -> {more, 16#d5, 16#06}; -dec_huffman_lookup(16#c6, 16#a) -> {more, 16#d5, 16#0a}; -dec_huffman_lookup(16#c6, 16#b) -> {more, 16#d5, 16#0f}; -dec_huffman_lookup(16#c6, 16#c) -> {more, 16#d5, 16#18}; -dec_huffman_lookup(16#c6, 16#d) -> {more, 16#d5, 16#1f}; -dec_huffman_lookup(16#c6, 16#e) -> {more, 16#d5, 16#29}; -dec_huffman_lookup(16#c6, 16#f) -> {ok, 16#d5, 16#38}; -dec_huffman_lookup(16#c7, 16#0) -> {more, 16#da, 16#01}; -dec_huffman_lookup(16#c7, 16#1) -> {ok, 16#da, 16#16}; -dec_huffman_lookup(16#c7, 16#2) -> {more, 16#db, 16#01}; -dec_huffman_lookup(16#c7, 16#3) -> {ok, 16#db, 16#16}; -dec_huffman_lookup(16#c7, 16#4) -> {more, 16#ee, 16#01}; -dec_huffman_lookup(16#c7, 16#5) -> {ok, 16#ee, 16#16}; -dec_huffman_lookup(16#c7, 16#6) -> {more, 16#f0, 16#01}; -dec_huffman_lookup(16#c7, 16#7) -> {ok, 16#f0, 16#16}; -dec_huffman_lookup(16#c7, 16#8) -> {more, 16#f2, 16#01}; -dec_huffman_lookup(16#c7, 16#9) -> {ok, 16#f2, 16#16}; -dec_huffman_lookup(16#c7, 16#a) -> {more, 16#f3, 16#01}; -dec_huffman_lookup(16#c7, 16#b) -> {ok, 16#f3, 16#16}; -dec_huffman_lookup(16#c7, 16#c) -> {more, 16#ff, 16#01}; -dec_huffman_lookup(16#c7, 16#d) -> {ok, 16#ff, 16#16}; -dec_huffman_lookup(16#c7, 16#e) -> {ok, 16#cb, 16#00}; -dec_huffman_lookup(16#c7, 16#f) -> {ok, 16#cc, 16#00}; -dec_huffman_lookup(16#c8, 16#0) -> {more, 16#da, 16#02}; -dec_huffman_lookup(16#c8, 16#1) -> {more, 16#da, 16#09}; -dec_huffman_lookup(16#c8, 16#2) -> {more, 16#da, 16#17}; -dec_huffman_lookup(16#c8, 16#3) -> {ok, 16#da, 16#28}; -dec_huffman_lookup(16#c8, 16#4) -> {more, 16#db, 16#02}; -dec_huffman_lookup(16#c8, 16#5) -> {more, 16#db, 16#09}; -dec_huffman_lookup(16#c8, 16#6) -> {more, 16#db, 16#17}; -dec_huffman_lookup(16#c8, 16#7) -> {ok, 16#db, 16#28}; -dec_huffman_lookup(16#c8, 16#8) -> {more, 16#ee, 16#02}; -dec_huffman_lookup(16#c8, 16#9) -> {more, 16#ee, 16#09}; -dec_huffman_lookup(16#c8, 16#a) -> {more, 16#ee, 16#17}; -dec_huffman_lookup(16#c8, 16#b) -> {ok, 16#ee, 16#28}; -dec_huffman_lookup(16#c8, 16#c) -> {more, 16#f0, 16#02}; -dec_huffman_lookup(16#c8, 16#d) -> {more, 16#f0, 16#09}; -dec_huffman_lookup(16#c8, 16#e) -> {more, 16#f0, 16#17}; -dec_huffman_lookup(16#c8, 16#f) -> {ok, 16#f0, 16#28}; -dec_huffman_lookup(16#c9, 16#0) -> {more, 16#da, 16#03}; -dec_huffman_lookup(16#c9, 16#1) -> {more, 16#da, 16#06}; -dec_huffman_lookup(16#c9, 16#2) -> {more, 16#da, 16#0a}; -dec_huffman_lookup(16#c9, 16#3) -> {more, 16#da, 16#0f}; -dec_huffman_lookup(16#c9, 16#4) -> {more, 16#da, 16#18}; -dec_huffman_lookup(16#c9, 16#5) -> {more, 16#da, 16#1f}; -dec_huffman_lookup(16#c9, 16#6) -> {more, 16#da, 16#29}; -dec_huffman_lookup(16#c9, 16#7) -> {ok, 16#da, 16#38}; -dec_huffman_lookup(16#c9, 16#8) -> {more, 16#db, 16#03}; -dec_huffman_lookup(16#c9, 16#9) -> {more, 16#db, 16#06}; -dec_huffman_lookup(16#c9, 16#a) -> {more, 16#db, 16#0a}; -dec_huffman_lookup(16#c9, 16#b) -> {more, 16#db, 16#0f}; -dec_huffman_lookup(16#c9, 16#c) -> {more, 16#db, 16#18}; -dec_huffman_lookup(16#c9, 16#d) -> {more, 16#db, 16#1f}; -dec_huffman_lookup(16#c9, 16#e) -> {more, 16#db, 16#29}; -dec_huffman_lookup(16#c9, 16#f) -> {ok, 16#db, 16#38}; -dec_huffman_lookup(16#ca, 16#0) -> {more, 16#ee, 16#03}; -dec_huffman_lookup(16#ca, 16#1) -> {more, 16#ee, 16#06}; -dec_huffman_lookup(16#ca, 16#2) -> {more, 16#ee, 16#0a}; -dec_huffman_lookup(16#ca, 16#3) -> {more, 16#ee, 16#0f}; -dec_huffman_lookup(16#ca, 16#4) -> {more, 16#ee, 16#18}; -dec_huffman_lookup(16#ca, 16#5) -> {more, 16#ee, 16#1f}; -dec_huffman_lookup(16#ca, 16#6) -> {more, 16#ee, 16#29}; -dec_huffman_lookup(16#ca, 16#7) -> {ok, 16#ee, 16#38}; -dec_huffman_lookup(16#ca, 16#8) -> {more, 16#f0, 16#03}; -dec_huffman_lookup(16#ca, 16#9) -> {more, 16#f0, 16#06}; -dec_huffman_lookup(16#ca, 16#a) -> {more, 16#f0, 16#0a}; -dec_huffman_lookup(16#ca, 16#b) -> {more, 16#f0, 16#0f}; -dec_huffman_lookup(16#ca, 16#c) -> {more, 16#f0, 16#18}; -dec_huffman_lookup(16#ca, 16#d) -> {more, 16#f0, 16#1f}; -dec_huffman_lookup(16#ca, 16#e) -> {more, 16#f0, 16#29}; -dec_huffman_lookup(16#ca, 16#f) -> {ok, 16#f0, 16#38}; -dec_huffman_lookup(16#cb, 16#0) -> {more, 16#f2, 16#02}; -dec_huffman_lookup(16#cb, 16#1) -> {more, 16#f2, 16#09}; -dec_huffman_lookup(16#cb, 16#2) -> {more, 16#f2, 16#17}; -dec_huffman_lookup(16#cb, 16#3) -> {ok, 16#f2, 16#28}; -dec_huffman_lookup(16#cb, 16#4) -> {more, 16#f3, 16#02}; -dec_huffman_lookup(16#cb, 16#5) -> {more, 16#f3, 16#09}; -dec_huffman_lookup(16#cb, 16#6) -> {more, 16#f3, 16#17}; -dec_huffman_lookup(16#cb, 16#7) -> {ok, 16#f3, 16#28}; -dec_huffman_lookup(16#cb, 16#8) -> {more, 16#ff, 16#02}; -dec_huffman_lookup(16#cb, 16#9) -> {more, 16#ff, 16#09}; -dec_huffman_lookup(16#cb, 16#a) -> {more, 16#ff, 16#17}; -dec_huffman_lookup(16#cb, 16#b) -> {ok, 16#ff, 16#28}; -dec_huffman_lookup(16#cb, 16#c) -> {more, 16#cb, 16#01}; -dec_huffman_lookup(16#cb, 16#d) -> {ok, 16#cb, 16#16}; -dec_huffman_lookup(16#cb, 16#e) -> {more, 16#cc, 16#01}; -dec_huffman_lookup(16#cb, 16#f) -> {ok, 16#cc, 16#16}; -dec_huffman_lookup(16#cc, 16#0) -> {more, 16#f2, 16#03}; -dec_huffman_lookup(16#cc, 16#1) -> {more, 16#f2, 16#06}; -dec_huffman_lookup(16#cc, 16#2) -> {more, 16#f2, 16#0a}; -dec_huffman_lookup(16#cc, 16#3) -> {more, 16#f2, 16#0f}; -dec_huffman_lookup(16#cc, 16#4) -> {more, 16#f2, 16#18}; -dec_huffman_lookup(16#cc, 16#5) -> {more, 16#f2, 16#1f}; -dec_huffman_lookup(16#cc, 16#6) -> {more, 16#f2, 16#29}; -dec_huffman_lookup(16#cc, 16#7) -> {ok, 16#f2, 16#38}; -dec_huffman_lookup(16#cc, 16#8) -> {more, 16#f3, 16#03}; -dec_huffman_lookup(16#cc, 16#9) -> {more, 16#f3, 16#06}; -dec_huffman_lookup(16#cc, 16#a) -> {more, 16#f3, 16#0a}; -dec_huffman_lookup(16#cc, 16#b) -> {more, 16#f3, 16#0f}; -dec_huffman_lookup(16#cc, 16#c) -> {more, 16#f3, 16#18}; -dec_huffman_lookup(16#cc, 16#d) -> {more, 16#f3, 16#1f}; -dec_huffman_lookup(16#cc, 16#e) -> {more, 16#f3, 16#29}; -dec_huffman_lookup(16#cc, 16#f) -> {ok, 16#f3, 16#38}; -dec_huffman_lookup(16#cd, 16#0) -> {more, 16#ff, 16#03}; -dec_huffman_lookup(16#cd, 16#1) -> {more, 16#ff, 16#06}; -dec_huffman_lookup(16#cd, 16#2) -> {more, 16#ff, 16#0a}; -dec_huffman_lookup(16#cd, 16#3) -> {more, 16#ff, 16#0f}; -dec_huffman_lookup(16#cd, 16#4) -> {more, 16#ff, 16#18}; -dec_huffman_lookup(16#cd, 16#5) -> {more, 16#ff, 16#1f}; -dec_huffman_lookup(16#cd, 16#6) -> {more, 16#ff, 16#29}; -dec_huffman_lookup(16#cd, 16#7) -> {ok, 16#ff, 16#38}; -dec_huffman_lookup(16#cd, 16#8) -> {more, 16#cb, 16#02}; -dec_huffman_lookup(16#cd, 16#9) -> {more, 16#cb, 16#09}; -dec_huffman_lookup(16#cd, 16#a) -> {more, 16#cb, 16#17}; -dec_huffman_lookup(16#cd, 16#b) -> {ok, 16#cb, 16#28}; -dec_huffman_lookup(16#cd, 16#c) -> {more, 16#cc, 16#02}; -dec_huffman_lookup(16#cd, 16#d) -> {more, 16#cc, 16#09}; -dec_huffman_lookup(16#cd, 16#e) -> {more, 16#cc, 16#17}; -dec_huffman_lookup(16#cd, 16#f) -> {ok, 16#cc, 16#28}; -dec_huffman_lookup(16#ce, 16#0) -> {more, 16#cb, 16#03}; -dec_huffman_lookup(16#ce, 16#1) -> {more, 16#cb, 16#06}; -dec_huffman_lookup(16#ce, 16#2) -> {more, 16#cb, 16#0a}; -dec_huffman_lookup(16#ce, 16#3) -> {more, 16#cb, 16#0f}; -dec_huffman_lookup(16#ce, 16#4) -> {more, 16#cb, 16#18}; -dec_huffman_lookup(16#ce, 16#5) -> {more, 16#cb, 16#1f}; -dec_huffman_lookup(16#ce, 16#6) -> {more, 16#cb, 16#29}; -dec_huffman_lookup(16#ce, 16#7) -> {ok, 16#cb, 16#38}; -dec_huffman_lookup(16#ce, 16#8) -> {more, 16#cc, 16#03}; -dec_huffman_lookup(16#ce, 16#9) -> {more, 16#cc, 16#06}; -dec_huffman_lookup(16#ce, 16#a) -> {more, 16#cc, 16#0a}; -dec_huffman_lookup(16#ce, 16#b) -> {more, 16#cc, 16#0f}; -dec_huffman_lookup(16#ce, 16#c) -> {more, 16#cc, 16#18}; -dec_huffman_lookup(16#ce, 16#d) -> {more, 16#cc, 16#1f}; -dec_huffman_lookup(16#ce, 16#e) -> {more, 16#cc, 16#29}; -dec_huffman_lookup(16#ce, 16#f) -> {ok, 16#cc, 16#38}; -dec_huffman_lookup(16#cf, 16#0) -> {more, undefined, 16#d3}; -dec_huffman_lookup(16#cf, 16#1) -> {more, undefined, 16#d4}; -dec_huffman_lookup(16#cf, 16#2) -> {more, undefined, 16#d6}; -dec_huffman_lookup(16#cf, 16#3) -> {more, undefined, 16#d7}; -dec_huffman_lookup(16#cf, 16#4) -> {more, undefined, 16#da}; -dec_huffman_lookup(16#cf, 16#5) -> {more, undefined, 16#db}; -dec_huffman_lookup(16#cf, 16#6) -> {more, undefined, 16#dd}; -dec_huffman_lookup(16#cf, 16#7) -> {more, undefined, 16#de}; -dec_huffman_lookup(16#cf, 16#8) -> {more, undefined, 16#e2}; -dec_huffman_lookup(16#cf, 16#9) -> {more, undefined, 16#e4}; -dec_huffman_lookup(16#cf, 16#a) -> {more, undefined, 16#e8}; -dec_huffman_lookup(16#cf, 16#b) -> {more, undefined, 16#eb}; -dec_huffman_lookup(16#cf, 16#c) -> {more, undefined, 16#f0}; -dec_huffman_lookup(16#cf, 16#d) -> {more, undefined, 16#f3}; -dec_huffman_lookup(16#cf, 16#e) -> {more, undefined, 16#f7}; -dec_huffman_lookup(16#cf, 16#f) -> {ok, undefined, 16#fa}; -dec_huffman_lookup(16#d0, 16#0) -> {ok, 16#d3, 16#00}; -dec_huffman_lookup(16#d0, 16#1) -> {ok, 16#d4, 16#00}; -dec_huffman_lookup(16#d0, 16#2) -> {ok, 16#d6, 16#00}; -dec_huffman_lookup(16#d0, 16#3) -> {ok, 16#dd, 16#00}; -dec_huffman_lookup(16#d0, 16#4) -> {ok, 16#de, 16#00}; -dec_huffman_lookup(16#d0, 16#5) -> {ok, 16#df, 16#00}; -dec_huffman_lookup(16#d0, 16#6) -> {ok, 16#f1, 16#00}; -dec_huffman_lookup(16#d0, 16#7) -> {ok, 16#f4, 16#00}; -dec_huffman_lookup(16#d0, 16#8) -> {ok, 16#f5, 16#00}; -dec_huffman_lookup(16#d0, 16#9) -> {ok, 16#f6, 16#00}; -dec_huffman_lookup(16#d0, 16#a) -> {ok, 16#f7, 16#00}; -dec_huffman_lookup(16#d0, 16#b) -> {ok, 16#f8, 16#00}; -dec_huffman_lookup(16#d0, 16#c) -> {ok, 16#fa, 16#00}; -dec_huffman_lookup(16#d0, 16#d) -> {ok, 16#fb, 16#00}; -dec_huffman_lookup(16#d0, 16#e) -> {ok, 16#fc, 16#00}; -dec_huffman_lookup(16#d0, 16#f) -> {ok, 16#fd, 16#00}; -dec_huffman_lookup(16#d1, 16#0) -> {more, 16#d3, 16#01}; -dec_huffman_lookup(16#d1, 16#1) -> {ok, 16#d3, 16#16}; -dec_huffman_lookup(16#d1, 16#2) -> {more, 16#d4, 16#01}; -dec_huffman_lookup(16#d1, 16#3) -> {ok, 16#d4, 16#16}; -dec_huffman_lookup(16#d1, 16#4) -> {more, 16#d6, 16#01}; -dec_huffman_lookup(16#d1, 16#5) -> {ok, 16#d6, 16#16}; -dec_huffman_lookup(16#d1, 16#6) -> {more, 16#dd, 16#01}; -dec_huffman_lookup(16#d1, 16#7) -> {ok, 16#dd, 16#16}; -dec_huffman_lookup(16#d1, 16#8) -> {more, 16#de, 16#01}; -dec_huffman_lookup(16#d1, 16#9) -> {ok, 16#de, 16#16}; -dec_huffman_lookup(16#d1, 16#a) -> {more, 16#df, 16#01}; -dec_huffman_lookup(16#d1, 16#b) -> {ok, 16#df, 16#16}; -dec_huffman_lookup(16#d1, 16#c) -> {more, 16#f1, 16#01}; -dec_huffman_lookup(16#d1, 16#d) -> {ok, 16#f1, 16#16}; -dec_huffman_lookup(16#d1, 16#e) -> {more, 16#f4, 16#01}; -dec_huffman_lookup(16#d1, 16#f) -> {ok, 16#f4, 16#16}; -dec_huffman_lookup(16#d2, 16#0) -> {more, 16#d3, 16#02}; -dec_huffman_lookup(16#d2, 16#1) -> {more, 16#d3, 16#09}; -dec_huffman_lookup(16#d2, 16#2) -> {more, 16#d3, 16#17}; -dec_huffman_lookup(16#d2, 16#3) -> {ok, 16#d3, 16#28}; -dec_huffman_lookup(16#d2, 16#4) -> {more, 16#d4, 16#02}; -dec_huffman_lookup(16#d2, 16#5) -> {more, 16#d4, 16#09}; -dec_huffman_lookup(16#d2, 16#6) -> {more, 16#d4, 16#17}; -dec_huffman_lookup(16#d2, 16#7) -> {ok, 16#d4, 16#28}; -dec_huffman_lookup(16#d2, 16#8) -> {more, 16#d6, 16#02}; -dec_huffman_lookup(16#d2, 16#9) -> {more, 16#d6, 16#09}; -dec_huffman_lookup(16#d2, 16#a) -> {more, 16#d6, 16#17}; -dec_huffman_lookup(16#d2, 16#b) -> {ok, 16#d6, 16#28}; -dec_huffman_lookup(16#d2, 16#c) -> {more, 16#dd, 16#02}; -dec_huffman_lookup(16#d2, 16#d) -> {more, 16#dd, 16#09}; -dec_huffman_lookup(16#d2, 16#e) -> {more, 16#dd, 16#17}; -dec_huffman_lookup(16#d2, 16#f) -> {ok, 16#dd, 16#28}; -dec_huffman_lookup(16#d3, 16#0) -> {more, 16#d3, 16#03}; -dec_huffman_lookup(16#d3, 16#1) -> {more, 16#d3, 16#06}; -dec_huffman_lookup(16#d3, 16#2) -> {more, 16#d3, 16#0a}; -dec_huffman_lookup(16#d3, 16#3) -> {more, 16#d3, 16#0f}; -dec_huffman_lookup(16#d3, 16#4) -> {more, 16#d3, 16#18}; -dec_huffman_lookup(16#d3, 16#5) -> {more, 16#d3, 16#1f}; -dec_huffman_lookup(16#d3, 16#6) -> {more, 16#d3, 16#29}; -dec_huffman_lookup(16#d3, 16#7) -> {ok, 16#d3, 16#38}; -dec_huffman_lookup(16#d3, 16#8) -> {more, 16#d4, 16#03}; -dec_huffman_lookup(16#d3, 16#9) -> {more, 16#d4, 16#06}; -dec_huffman_lookup(16#d3, 16#a) -> {more, 16#d4, 16#0a}; -dec_huffman_lookup(16#d3, 16#b) -> {more, 16#d4, 16#0f}; -dec_huffman_lookup(16#d3, 16#c) -> {more, 16#d4, 16#18}; -dec_huffman_lookup(16#d3, 16#d) -> {more, 16#d4, 16#1f}; -dec_huffman_lookup(16#d3, 16#e) -> {more, 16#d4, 16#29}; -dec_huffman_lookup(16#d3, 16#f) -> {ok, 16#d4, 16#38}; -dec_huffman_lookup(16#d4, 16#0) -> {more, 16#d6, 16#03}; -dec_huffman_lookup(16#d4, 16#1) -> {more, 16#d6, 16#06}; -dec_huffman_lookup(16#d4, 16#2) -> {more, 16#d6, 16#0a}; -dec_huffman_lookup(16#d4, 16#3) -> {more, 16#d6, 16#0f}; -dec_huffman_lookup(16#d4, 16#4) -> {more, 16#d6, 16#18}; -dec_huffman_lookup(16#d4, 16#5) -> {more, 16#d6, 16#1f}; -dec_huffman_lookup(16#d4, 16#6) -> {more, 16#d6, 16#29}; -dec_huffman_lookup(16#d4, 16#7) -> {ok, 16#d6, 16#38}; -dec_huffman_lookup(16#d4, 16#8) -> {more, 16#dd, 16#03}; -dec_huffman_lookup(16#d4, 16#9) -> {more, 16#dd, 16#06}; -dec_huffman_lookup(16#d4, 16#a) -> {more, 16#dd, 16#0a}; -dec_huffman_lookup(16#d4, 16#b) -> {more, 16#dd, 16#0f}; -dec_huffman_lookup(16#d4, 16#c) -> {more, 16#dd, 16#18}; -dec_huffman_lookup(16#d4, 16#d) -> {more, 16#dd, 16#1f}; -dec_huffman_lookup(16#d4, 16#e) -> {more, 16#dd, 16#29}; -dec_huffman_lookup(16#d4, 16#f) -> {ok, 16#dd, 16#38}; -dec_huffman_lookup(16#d5, 16#0) -> {more, 16#de, 16#02}; -dec_huffman_lookup(16#d5, 16#1) -> {more, 16#de, 16#09}; -dec_huffman_lookup(16#d5, 16#2) -> {more, 16#de, 16#17}; -dec_huffman_lookup(16#d5, 16#3) -> {ok, 16#de, 16#28}; -dec_huffman_lookup(16#d5, 16#4) -> {more, 16#df, 16#02}; -dec_huffman_lookup(16#d5, 16#5) -> {more, 16#df, 16#09}; -dec_huffman_lookup(16#d5, 16#6) -> {more, 16#df, 16#17}; -dec_huffman_lookup(16#d5, 16#7) -> {ok, 16#df, 16#28}; -dec_huffman_lookup(16#d5, 16#8) -> {more, 16#f1, 16#02}; -dec_huffman_lookup(16#d5, 16#9) -> {more, 16#f1, 16#09}; -dec_huffman_lookup(16#d5, 16#a) -> {more, 16#f1, 16#17}; -dec_huffman_lookup(16#d5, 16#b) -> {ok, 16#f1, 16#28}; -dec_huffman_lookup(16#d5, 16#c) -> {more, 16#f4, 16#02}; -dec_huffman_lookup(16#d5, 16#d) -> {more, 16#f4, 16#09}; -dec_huffman_lookup(16#d5, 16#e) -> {more, 16#f4, 16#17}; -dec_huffman_lookup(16#d5, 16#f) -> {ok, 16#f4, 16#28}; -dec_huffman_lookup(16#d6, 16#0) -> {more, 16#de, 16#03}; -dec_huffman_lookup(16#d6, 16#1) -> {more, 16#de, 16#06}; -dec_huffman_lookup(16#d6, 16#2) -> {more, 16#de, 16#0a}; -dec_huffman_lookup(16#d6, 16#3) -> {more, 16#de, 16#0f}; -dec_huffman_lookup(16#d6, 16#4) -> {more, 16#de, 16#18}; -dec_huffman_lookup(16#d6, 16#5) -> {more, 16#de, 16#1f}; -dec_huffman_lookup(16#d6, 16#6) -> {more, 16#de, 16#29}; -dec_huffman_lookup(16#d6, 16#7) -> {ok, 16#de, 16#38}; -dec_huffman_lookup(16#d6, 16#8) -> {more, 16#df, 16#03}; -dec_huffman_lookup(16#d6, 16#9) -> {more, 16#df, 16#06}; -dec_huffman_lookup(16#d6, 16#a) -> {more, 16#df, 16#0a}; -dec_huffman_lookup(16#d6, 16#b) -> {more, 16#df, 16#0f}; -dec_huffman_lookup(16#d6, 16#c) -> {more, 16#df, 16#18}; -dec_huffman_lookup(16#d6, 16#d) -> {more, 16#df, 16#1f}; -dec_huffman_lookup(16#d6, 16#e) -> {more, 16#df, 16#29}; -dec_huffman_lookup(16#d6, 16#f) -> {ok, 16#df, 16#38}; -dec_huffman_lookup(16#d7, 16#0) -> {more, 16#f1, 16#03}; -dec_huffman_lookup(16#d7, 16#1) -> {more, 16#f1, 16#06}; -dec_huffman_lookup(16#d7, 16#2) -> {more, 16#f1, 16#0a}; -dec_huffman_lookup(16#d7, 16#3) -> {more, 16#f1, 16#0f}; -dec_huffman_lookup(16#d7, 16#4) -> {more, 16#f1, 16#18}; -dec_huffman_lookup(16#d7, 16#5) -> {more, 16#f1, 16#1f}; -dec_huffman_lookup(16#d7, 16#6) -> {more, 16#f1, 16#29}; -dec_huffman_lookup(16#d7, 16#7) -> {ok, 16#f1, 16#38}; -dec_huffman_lookup(16#d7, 16#8) -> {more, 16#f4, 16#03}; -dec_huffman_lookup(16#d7, 16#9) -> {more, 16#f4, 16#06}; -dec_huffman_lookup(16#d7, 16#a) -> {more, 16#f4, 16#0a}; -dec_huffman_lookup(16#d7, 16#b) -> {more, 16#f4, 16#0f}; -dec_huffman_lookup(16#d7, 16#c) -> {more, 16#f4, 16#18}; -dec_huffman_lookup(16#d7, 16#d) -> {more, 16#f4, 16#1f}; -dec_huffman_lookup(16#d7, 16#e) -> {more, 16#f4, 16#29}; -dec_huffman_lookup(16#d7, 16#f) -> {ok, 16#f4, 16#38}; -dec_huffman_lookup(16#d8, 16#0) -> {more, 16#f5, 16#01}; -dec_huffman_lookup(16#d8, 16#1) -> {ok, 16#f5, 16#16}; -dec_huffman_lookup(16#d8, 16#2) -> {more, 16#f6, 16#01}; -dec_huffman_lookup(16#d8, 16#3) -> {ok, 16#f6, 16#16}; -dec_huffman_lookup(16#d8, 16#4) -> {more, 16#f7, 16#01}; -dec_huffman_lookup(16#d8, 16#5) -> {ok, 16#f7, 16#16}; -dec_huffman_lookup(16#d8, 16#6) -> {more, 16#f8, 16#01}; -dec_huffman_lookup(16#d8, 16#7) -> {ok, 16#f8, 16#16}; -dec_huffman_lookup(16#d8, 16#8) -> {more, 16#fa, 16#01}; -dec_huffman_lookup(16#d8, 16#9) -> {ok, 16#fa, 16#16}; -dec_huffman_lookup(16#d8, 16#a) -> {more, 16#fb, 16#01}; -dec_huffman_lookup(16#d8, 16#b) -> {ok, 16#fb, 16#16}; -dec_huffman_lookup(16#d8, 16#c) -> {more, 16#fc, 16#01}; -dec_huffman_lookup(16#d8, 16#d) -> {ok, 16#fc, 16#16}; -dec_huffman_lookup(16#d8, 16#e) -> {more, 16#fd, 16#01}; -dec_huffman_lookup(16#d8, 16#f) -> {ok, 16#fd, 16#16}; -dec_huffman_lookup(16#d9, 16#0) -> {more, 16#f5, 16#02}; -dec_huffman_lookup(16#d9, 16#1) -> {more, 16#f5, 16#09}; -dec_huffman_lookup(16#d9, 16#2) -> {more, 16#f5, 16#17}; -dec_huffman_lookup(16#d9, 16#3) -> {ok, 16#f5, 16#28}; -dec_huffman_lookup(16#d9, 16#4) -> {more, 16#f6, 16#02}; -dec_huffman_lookup(16#d9, 16#5) -> {more, 16#f6, 16#09}; -dec_huffman_lookup(16#d9, 16#6) -> {more, 16#f6, 16#17}; -dec_huffman_lookup(16#d9, 16#7) -> {ok, 16#f6, 16#28}; -dec_huffman_lookup(16#d9, 16#8) -> {more, 16#f7, 16#02}; -dec_huffman_lookup(16#d9, 16#9) -> {more, 16#f7, 16#09}; -dec_huffman_lookup(16#d9, 16#a) -> {more, 16#f7, 16#17}; -dec_huffman_lookup(16#d9, 16#b) -> {ok, 16#f7, 16#28}; -dec_huffman_lookup(16#d9, 16#c) -> {more, 16#f8, 16#02}; -dec_huffman_lookup(16#d9, 16#d) -> {more, 16#f8, 16#09}; -dec_huffman_lookup(16#d9, 16#e) -> {more, 16#f8, 16#17}; -dec_huffman_lookup(16#d9, 16#f) -> {ok, 16#f8, 16#28}; -dec_huffman_lookup(16#da, 16#0) -> {more, 16#f5, 16#03}; -dec_huffman_lookup(16#da, 16#1) -> {more, 16#f5, 16#06}; -dec_huffman_lookup(16#da, 16#2) -> {more, 16#f5, 16#0a}; -dec_huffman_lookup(16#da, 16#3) -> {more, 16#f5, 16#0f}; -dec_huffman_lookup(16#da, 16#4) -> {more, 16#f5, 16#18}; -dec_huffman_lookup(16#da, 16#5) -> {more, 16#f5, 16#1f}; -dec_huffman_lookup(16#da, 16#6) -> {more, 16#f5, 16#29}; -dec_huffman_lookup(16#da, 16#7) -> {ok, 16#f5, 16#38}; -dec_huffman_lookup(16#da, 16#8) -> {more, 16#f6, 16#03}; -dec_huffman_lookup(16#da, 16#9) -> {more, 16#f6, 16#06}; -dec_huffman_lookup(16#da, 16#a) -> {more, 16#f6, 16#0a}; -dec_huffman_lookup(16#da, 16#b) -> {more, 16#f6, 16#0f}; -dec_huffman_lookup(16#da, 16#c) -> {more, 16#f6, 16#18}; -dec_huffman_lookup(16#da, 16#d) -> {more, 16#f6, 16#1f}; -dec_huffman_lookup(16#da, 16#e) -> {more, 16#f6, 16#29}; -dec_huffman_lookup(16#da, 16#f) -> {ok, 16#f6, 16#38}; -dec_huffman_lookup(16#db, 16#0) -> {more, 16#f7, 16#03}; -dec_huffman_lookup(16#db, 16#1) -> {more, 16#f7, 16#06}; -dec_huffman_lookup(16#db, 16#2) -> {more, 16#f7, 16#0a}; -dec_huffman_lookup(16#db, 16#3) -> {more, 16#f7, 16#0f}; -dec_huffman_lookup(16#db, 16#4) -> {more, 16#f7, 16#18}; -dec_huffman_lookup(16#db, 16#5) -> {more, 16#f7, 16#1f}; -dec_huffman_lookup(16#db, 16#6) -> {more, 16#f7, 16#29}; -dec_huffman_lookup(16#db, 16#7) -> {ok, 16#f7, 16#38}; -dec_huffman_lookup(16#db, 16#8) -> {more, 16#f8, 16#03}; -dec_huffman_lookup(16#db, 16#9) -> {more, 16#f8, 16#06}; -dec_huffman_lookup(16#db, 16#a) -> {more, 16#f8, 16#0a}; -dec_huffman_lookup(16#db, 16#b) -> {more, 16#f8, 16#0f}; -dec_huffman_lookup(16#db, 16#c) -> {more, 16#f8, 16#18}; -dec_huffman_lookup(16#db, 16#d) -> {more, 16#f8, 16#1f}; -dec_huffman_lookup(16#db, 16#e) -> {more, 16#f8, 16#29}; -dec_huffman_lookup(16#db, 16#f) -> {ok, 16#f8, 16#38}; -dec_huffman_lookup(16#dc, 16#0) -> {more, 16#fa, 16#02}; -dec_huffman_lookup(16#dc, 16#1) -> {more, 16#fa, 16#09}; -dec_huffman_lookup(16#dc, 16#2) -> {more, 16#fa, 16#17}; -dec_huffman_lookup(16#dc, 16#3) -> {ok, 16#fa, 16#28}; -dec_huffman_lookup(16#dc, 16#4) -> {more, 16#fb, 16#02}; -dec_huffman_lookup(16#dc, 16#5) -> {more, 16#fb, 16#09}; -dec_huffman_lookup(16#dc, 16#6) -> {more, 16#fb, 16#17}; -dec_huffman_lookup(16#dc, 16#7) -> {ok, 16#fb, 16#28}; -dec_huffman_lookup(16#dc, 16#8) -> {more, 16#fc, 16#02}; -dec_huffman_lookup(16#dc, 16#9) -> {more, 16#fc, 16#09}; -dec_huffman_lookup(16#dc, 16#a) -> {more, 16#fc, 16#17}; -dec_huffman_lookup(16#dc, 16#b) -> {ok, 16#fc, 16#28}; -dec_huffman_lookup(16#dc, 16#c) -> {more, 16#fd, 16#02}; -dec_huffman_lookup(16#dc, 16#d) -> {more, 16#fd, 16#09}; -dec_huffman_lookup(16#dc, 16#e) -> {more, 16#fd, 16#17}; -dec_huffman_lookup(16#dc, 16#f) -> {ok, 16#fd, 16#28}; -dec_huffman_lookup(16#dd, 16#0) -> {more, 16#fa, 16#03}; -dec_huffman_lookup(16#dd, 16#1) -> {more, 16#fa, 16#06}; -dec_huffman_lookup(16#dd, 16#2) -> {more, 16#fa, 16#0a}; -dec_huffman_lookup(16#dd, 16#3) -> {more, 16#fa, 16#0f}; -dec_huffman_lookup(16#dd, 16#4) -> {more, 16#fa, 16#18}; -dec_huffman_lookup(16#dd, 16#5) -> {more, 16#fa, 16#1f}; -dec_huffman_lookup(16#dd, 16#6) -> {more, 16#fa, 16#29}; -dec_huffman_lookup(16#dd, 16#7) -> {ok, 16#fa, 16#38}; -dec_huffman_lookup(16#dd, 16#8) -> {more, 16#fb, 16#03}; -dec_huffman_lookup(16#dd, 16#9) -> {more, 16#fb, 16#06}; -dec_huffman_lookup(16#dd, 16#a) -> {more, 16#fb, 16#0a}; -dec_huffman_lookup(16#dd, 16#b) -> {more, 16#fb, 16#0f}; -dec_huffman_lookup(16#dd, 16#c) -> {more, 16#fb, 16#18}; -dec_huffman_lookup(16#dd, 16#d) -> {more, 16#fb, 16#1f}; -dec_huffman_lookup(16#dd, 16#e) -> {more, 16#fb, 16#29}; -dec_huffman_lookup(16#dd, 16#f) -> {ok, 16#fb, 16#38}; -dec_huffman_lookup(16#de, 16#0) -> {more, 16#fc, 16#03}; -dec_huffman_lookup(16#de, 16#1) -> {more, 16#fc, 16#06}; -dec_huffman_lookup(16#de, 16#2) -> {more, 16#fc, 16#0a}; -dec_huffman_lookup(16#de, 16#3) -> {more, 16#fc, 16#0f}; -dec_huffman_lookup(16#de, 16#4) -> {more, 16#fc, 16#18}; -dec_huffman_lookup(16#de, 16#5) -> {more, 16#fc, 16#1f}; -dec_huffman_lookup(16#de, 16#6) -> {more, 16#fc, 16#29}; -dec_huffman_lookup(16#de, 16#7) -> {ok, 16#fc, 16#38}; -dec_huffman_lookup(16#de, 16#8) -> {more, 16#fd, 16#03}; -dec_huffman_lookup(16#de, 16#9) -> {more, 16#fd, 16#06}; -dec_huffman_lookup(16#de, 16#a) -> {more, 16#fd, 16#0a}; -dec_huffman_lookup(16#de, 16#b) -> {more, 16#fd, 16#0f}; -dec_huffman_lookup(16#de, 16#c) -> {more, 16#fd, 16#18}; -dec_huffman_lookup(16#de, 16#d) -> {more, 16#fd, 16#1f}; -dec_huffman_lookup(16#de, 16#e) -> {more, 16#fd, 16#29}; -dec_huffman_lookup(16#de, 16#f) -> {ok, 16#fd, 16#38}; -dec_huffman_lookup(16#df, 16#0) -> {ok, 16#fe, 16#00}; -dec_huffman_lookup(16#df, 16#1) -> {more, undefined, 16#e3}; -dec_huffman_lookup(16#df, 16#2) -> {more, undefined, 16#e5}; -dec_huffman_lookup(16#df, 16#3) -> {more, undefined, 16#e6}; -dec_huffman_lookup(16#df, 16#4) -> {more, undefined, 16#e9}; -dec_huffman_lookup(16#df, 16#5) -> {more, undefined, 16#ea}; -dec_huffman_lookup(16#df, 16#6) -> {more, undefined, 16#ec}; -dec_huffman_lookup(16#df, 16#7) -> {more, undefined, 16#ed}; -dec_huffman_lookup(16#df, 16#8) -> {more, undefined, 16#f1}; -dec_huffman_lookup(16#df, 16#9) -> {more, undefined, 16#f2}; -dec_huffman_lookup(16#df, 16#a) -> {more, undefined, 16#f4}; -dec_huffman_lookup(16#df, 16#b) -> {more, undefined, 16#f5}; -dec_huffman_lookup(16#df, 16#c) -> {more, undefined, 16#f8}; -dec_huffman_lookup(16#df, 16#d) -> {more, undefined, 16#f9}; -dec_huffman_lookup(16#df, 16#e) -> {more, undefined, 16#fb}; -dec_huffman_lookup(16#df, 16#f) -> {ok, undefined, 16#fc}; -dec_huffman_lookup(16#e0, 16#0) -> {more, 16#fe, 16#01}; -dec_huffman_lookup(16#e0, 16#1) -> {ok, 16#fe, 16#16}; -dec_huffman_lookup(16#e0, 16#2) -> {ok, 16#02, 16#00}; -dec_huffman_lookup(16#e0, 16#3) -> {ok, 16#03, 16#00}; -dec_huffman_lookup(16#e0, 16#4) -> {ok, 16#04, 16#00}; -dec_huffman_lookup(16#e0, 16#5) -> {ok, 16#05, 16#00}; -dec_huffman_lookup(16#e0, 16#6) -> {ok, 16#06, 16#00}; -dec_huffman_lookup(16#e0, 16#7) -> {ok, 16#07, 16#00}; -dec_huffman_lookup(16#e0, 16#8) -> {ok, 16#08, 16#00}; -dec_huffman_lookup(16#e0, 16#9) -> {ok, 16#0b, 16#00}; -dec_huffman_lookup(16#e0, 16#a) -> {ok, 16#0c, 16#00}; -dec_huffman_lookup(16#e0, 16#b) -> {ok, 16#0e, 16#00}; -dec_huffman_lookup(16#e0, 16#c) -> {ok, 16#0f, 16#00}; -dec_huffman_lookup(16#e0, 16#d) -> {ok, 16#10, 16#00}; -dec_huffman_lookup(16#e0, 16#e) -> {ok, 16#11, 16#00}; -dec_huffman_lookup(16#e0, 16#f) -> {ok, 16#12, 16#00}; -dec_huffman_lookup(16#e1, 16#0) -> {more, 16#fe, 16#02}; -dec_huffman_lookup(16#e1, 16#1) -> {more, 16#fe, 16#09}; -dec_huffman_lookup(16#e1, 16#2) -> {more, 16#fe, 16#17}; -dec_huffman_lookup(16#e1, 16#3) -> {ok, 16#fe, 16#28}; -dec_huffman_lookup(16#e1, 16#4) -> {more, 16#02, 16#01}; -dec_huffman_lookup(16#e1, 16#5) -> {ok, 16#02, 16#16}; -dec_huffman_lookup(16#e1, 16#6) -> {more, 16#03, 16#01}; -dec_huffman_lookup(16#e1, 16#7) -> {ok, 16#03, 16#16}; -dec_huffman_lookup(16#e1, 16#8) -> {more, 16#04, 16#01}; -dec_huffman_lookup(16#e1, 16#9) -> {ok, 16#04, 16#16}; -dec_huffman_lookup(16#e1, 16#a) -> {more, 16#05, 16#01}; -dec_huffman_lookup(16#e1, 16#b) -> {ok, 16#05, 16#16}; -dec_huffman_lookup(16#e1, 16#c) -> {more, 16#06, 16#01}; -dec_huffman_lookup(16#e1, 16#d) -> {ok, 16#06, 16#16}; -dec_huffman_lookup(16#e1, 16#e) -> {more, 16#07, 16#01}; -dec_huffman_lookup(16#e1, 16#f) -> {ok, 16#07, 16#16}; -dec_huffman_lookup(16#e2, 16#0) -> {more, 16#fe, 16#03}; -dec_huffman_lookup(16#e2, 16#1) -> {more, 16#fe, 16#06}; -dec_huffman_lookup(16#e2, 16#2) -> {more, 16#fe, 16#0a}; -dec_huffman_lookup(16#e2, 16#3) -> {more, 16#fe, 16#0f}; -dec_huffman_lookup(16#e2, 16#4) -> {more, 16#fe, 16#18}; -dec_huffman_lookup(16#e2, 16#5) -> {more, 16#fe, 16#1f}; -dec_huffman_lookup(16#e2, 16#6) -> {more, 16#fe, 16#29}; -dec_huffman_lookup(16#e2, 16#7) -> {ok, 16#fe, 16#38}; -dec_huffman_lookup(16#e2, 16#8) -> {more, 16#02, 16#02}; -dec_huffman_lookup(16#e2, 16#9) -> {more, 16#02, 16#09}; -dec_huffman_lookup(16#e2, 16#a) -> {more, 16#02, 16#17}; -dec_huffman_lookup(16#e2, 16#b) -> {ok, 16#02, 16#28}; -dec_huffman_lookup(16#e2, 16#c) -> {more, 16#03, 16#02}; -dec_huffman_lookup(16#e2, 16#d) -> {more, 16#03, 16#09}; -dec_huffman_lookup(16#e2, 16#e) -> {more, 16#03, 16#17}; -dec_huffman_lookup(16#e2, 16#f) -> {ok, 16#03, 16#28}; -dec_huffman_lookup(16#e3, 16#0) -> {more, 16#02, 16#03}; -dec_huffman_lookup(16#e3, 16#1) -> {more, 16#02, 16#06}; -dec_huffman_lookup(16#e3, 16#2) -> {more, 16#02, 16#0a}; -dec_huffman_lookup(16#e3, 16#3) -> {more, 16#02, 16#0f}; -dec_huffman_lookup(16#e3, 16#4) -> {more, 16#02, 16#18}; -dec_huffman_lookup(16#e3, 16#5) -> {more, 16#02, 16#1f}; -dec_huffman_lookup(16#e3, 16#6) -> {more, 16#02, 16#29}; -dec_huffman_lookup(16#e3, 16#7) -> {ok, 16#02, 16#38}; -dec_huffman_lookup(16#e3, 16#8) -> {more, 16#03, 16#03}; -dec_huffman_lookup(16#e3, 16#9) -> {more, 16#03, 16#06}; -dec_huffman_lookup(16#e3, 16#a) -> {more, 16#03, 16#0a}; -dec_huffman_lookup(16#e3, 16#b) -> {more, 16#03, 16#0f}; -dec_huffman_lookup(16#e3, 16#c) -> {more, 16#03, 16#18}; -dec_huffman_lookup(16#e3, 16#d) -> {more, 16#03, 16#1f}; -dec_huffman_lookup(16#e3, 16#e) -> {more, 16#03, 16#29}; -dec_huffman_lookup(16#e3, 16#f) -> {ok, 16#03, 16#38}; -dec_huffman_lookup(16#e4, 16#0) -> {more, 16#04, 16#02}; -dec_huffman_lookup(16#e4, 16#1) -> {more, 16#04, 16#09}; -dec_huffman_lookup(16#e4, 16#2) -> {more, 16#04, 16#17}; -dec_huffman_lookup(16#e4, 16#3) -> {ok, 16#04, 16#28}; -dec_huffman_lookup(16#e4, 16#4) -> {more, 16#05, 16#02}; -dec_huffman_lookup(16#e4, 16#5) -> {more, 16#05, 16#09}; -dec_huffman_lookup(16#e4, 16#6) -> {more, 16#05, 16#17}; -dec_huffman_lookup(16#e4, 16#7) -> {ok, 16#05, 16#28}; -dec_huffman_lookup(16#e4, 16#8) -> {more, 16#06, 16#02}; -dec_huffman_lookup(16#e4, 16#9) -> {more, 16#06, 16#09}; -dec_huffman_lookup(16#e4, 16#a) -> {more, 16#06, 16#17}; -dec_huffman_lookup(16#e4, 16#b) -> {ok, 16#06, 16#28}; -dec_huffman_lookup(16#e4, 16#c) -> {more, 16#07, 16#02}; -dec_huffman_lookup(16#e4, 16#d) -> {more, 16#07, 16#09}; -dec_huffman_lookup(16#e4, 16#e) -> {more, 16#07, 16#17}; -dec_huffman_lookup(16#e4, 16#f) -> {ok, 16#07, 16#28}; -dec_huffman_lookup(16#e5, 16#0) -> {more, 16#04, 16#03}; -dec_huffman_lookup(16#e5, 16#1) -> {more, 16#04, 16#06}; -dec_huffman_lookup(16#e5, 16#2) -> {more, 16#04, 16#0a}; -dec_huffman_lookup(16#e5, 16#3) -> {more, 16#04, 16#0f}; -dec_huffman_lookup(16#e5, 16#4) -> {more, 16#04, 16#18}; -dec_huffman_lookup(16#e5, 16#5) -> {more, 16#04, 16#1f}; -dec_huffman_lookup(16#e5, 16#6) -> {more, 16#04, 16#29}; -dec_huffman_lookup(16#e5, 16#7) -> {ok, 16#04, 16#38}; -dec_huffman_lookup(16#e5, 16#8) -> {more, 16#05, 16#03}; -dec_huffman_lookup(16#e5, 16#9) -> {more, 16#05, 16#06}; -dec_huffman_lookup(16#e5, 16#a) -> {more, 16#05, 16#0a}; -dec_huffman_lookup(16#e5, 16#b) -> {more, 16#05, 16#0f}; -dec_huffman_lookup(16#e5, 16#c) -> {more, 16#05, 16#18}; -dec_huffman_lookup(16#e5, 16#d) -> {more, 16#05, 16#1f}; -dec_huffman_lookup(16#e5, 16#e) -> {more, 16#05, 16#29}; -dec_huffman_lookup(16#e5, 16#f) -> {ok, 16#05, 16#38}; -dec_huffman_lookup(16#e6, 16#0) -> {more, 16#06, 16#03}; -dec_huffman_lookup(16#e6, 16#1) -> {more, 16#06, 16#06}; -dec_huffman_lookup(16#e6, 16#2) -> {more, 16#06, 16#0a}; -dec_huffman_lookup(16#e6, 16#3) -> {more, 16#06, 16#0f}; -dec_huffman_lookup(16#e6, 16#4) -> {more, 16#06, 16#18}; -dec_huffman_lookup(16#e6, 16#5) -> {more, 16#06, 16#1f}; -dec_huffman_lookup(16#e6, 16#6) -> {more, 16#06, 16#29}; -dec_huffman_lookup(16#e6, 16#7) -> {ok, 16#06, 16#38}; -dec_huffman_lookup(16#e6, 16#8) -> {more, 16#07, 16#03}; -dec_huffman_lookup(16#e6, 16#9) -> {more, 16#07, 16#06}; -dec_huffman_lookup(16#e6, 16#a) -> {more, 16#07, 16#0a}; -dec_huffman_lookup(16#e6, 16#b) -> {more, 16#07, 16#0f}; -dec_huffman_lookup(16#e6, 16#c) -> {more, 16#07, 16#18}; -dec_huffman_lookup(16#e6, 16#d) -> {more, 16#07, 16#1f}; -dec_huffman_lookup(16#e6, 16#e) -> {more, 16#07, 16#29}; -dec_huffman_lookup(16#e6, 16#f) -> {ok, 16#07, 16#38}; -dec_huffman_lookup(16#e7, 16#0) -> {more, 16#08, 16#01}; -dec_huffman_lookup(16#e7, 16#1) -> {ok, 16#08, 16#16}; -dec_huffman_lookup(16#e7, 16#2) -> {more, 16#0b, 16#01}; -dec_huffman_lookup(16#e7, 16#3) -> {ok, 16#0b, 16#16}; -dec_huffman_lookup(16#e7, 16#4) -> {more, 16#0c, 16#01}; -dec_huffman_lookup(16#e7, 16#5) -> {ok, 16#0c, 16#16}; -dec_huffman_lookup(16#e7, 16#6) -> {more, 16#0e, 16#01}; -dec_huffman_lookup(16#e7, 16#7) -> {ok, 16#0e, 16#16}; -dec_huffman_lookup(16#e7, 16#8) -> {more, 16#0f, 16#01}; -dec_huffman_lookup(16#e7, 16#9) -> {ok, 16#0f, 16#16}; -dec_huffman_lookup(16#e7, 16#a) -> {more, 16#10, 16#01}; -dec_huffman_lookup(16#e7, 16#b) -> {ok, 16#10, 16#16}; -dec_huffman_lookup(16#e7, 16#c) -> {more, 16#11, 16#01}; -dec_huffman_lookup(16#e7, 16#d) -> {ok, 16#11, 16#16}; -dec_huffman_lookup(16#e7, 16#e) -> {more, 16#12, 16#01}; -dec_huffman_lookup(16#e7, 16#f) -> {ok, 16#12, 16#16}; -dec_huffman_lookup(16#e8, 16#0) -> {more, 16#08, 16#02}; -dec_huffman_lookup(16#e8, 16#1) -> {more, 16#08, 16#09}; -dec_huffman_lookup(16#e8, 16#2) -> {more, 16#08, 16#17}; -dec_huffman_lookup(16#e8, 16#3) -> {ok, 16#08, 16#28}; -dec_huffman_lookup(16#e8, 16#4) -> {more, 16#0b, 16#02}; -dec_huffman_lookup(16#e8, 16#5) -> {more, 16#0b, 16#09}; -dec_huffman_lookup(16#e8, 16#6) -> {more, 16#0b, 16#17}; -dec_huffman_lookup(16#e8, 16#7) -> {ok, 16#0b, 16#28}; -dec_huffman_lookup(16#e8, 16#8) -> {more, 16#0c, 16#02}; -dec_huffman_lookup(16#e8, 16#9) -> {more, 16#0c, 16#09}; -dec_huffman_lookup(16#e8, 16#a) -> {more, 16#0c, 16#17}; -dec_huffman_lookup(16#e8, 16#b) -> {ok, 16#0c, 16#28}; -dec_huffman_lookup(16#e8, 16#c) -> {more, 16#0e, 16#02}; -dec_huffman_lookup(16#e8, 16#d) -> {more, 16#0e, 16#09}; -dec_huffman_lookup(16#e8, 16#e) -> {more, 16#0e, 16#17}; -dec_huffman_lookup(16#e8, 16#f) -> {ok, 16#0e, 16#28}; -dec_huffman_lookup(16#e9, 16#0) -> {more, 16#08, 16#03}; -dec_huffman_lookup(16#e9, 16#1) -> {more, 16#08, 16#06}; -dec_huffman_lookup(16#e9, 16#2) -> {more, 16#08, 16#0a}; -dec_huffman_lookup(16#e9, 16#3) -> {more, 16#08, 16#0f}; -dec_huffman_lookup(16#e9, 16#4) -> {more, 16#08, 16#18}; -dec_huffman_lookup(16#e9, 16#5) -> {more, 16#08, 16#1f}; -dec_huffman_lookup(16#e9, 16#6) -> {more, 16#08, 16#29}; -dec_huffman_lookup(16#e9, 16#7) -> {ok, 16#08, 16#38}; -dec_huffman_lookup(16#e9, 16#8) -> {more, 16#0b, 16#03}; -dec_huffman_lookup(16#e9, 16#9) -> {more, 16#0b, 16#06}; -dec_huffman_lookup(16#e9, 16#a) -> {more, 16#0b, 16#0a}; -dec_huffman_lookup(16#e9, 16#b) -> {more, 16#0b, 16#0f}; -dec_huffman_lookup(16#e9, 16#c) -> {more, 16#0b, 16#18}; -dec_huffman_lookup(16#e9, 16#d) -> {more, 16#0b, 16#1f}; -dec_huffman_lookup(16#e9, 16#e) -> {more, 16#0b, 16#29}; -dec_huffman_lookup(16#e9, 16#f) -> {ok, 16#0b, 16#38}; -dec_huffman_lookup(16#ea, 16#0) -> {more, 16#0c, 16#03}; -dec_huffman_lookup(16#ea, 16#1) -> {more, 16#0c, 16#06}; -dec_huffman_lookup(16#ea, 16#2) -> {more, 16#0c, 16#0a}; -dec_huffman_lookup(16#ea, 16#3) -> {more, 16#0c, 16#0f}; -dec_huffman_lookup(16#ea, 16#4) -> {more, 16#0c, 16#18}; -dec_huffman_lookup(16#ea, 16#5) -> {more, 16#0c, 16#1f}; -dec_huffman_lookup(16#ea, 16#6) -> {more, 16#0c, 16#29}; -dec_huffman_lookup(16#ea, 16#7) -> {ok, 16#0c, 16#38}; -dec_huffman_lookup(16#ea, 16#8) -> {more, 16#0e, 16#03}; -dec_huffman_lookup(16#ea, 16#9) -> {more, 16#0e, 16#06}; -dec_huffman_lookup(16#ea, 16#a) -> {more, 16#0e, 16#0a}; -dec_huffman_lookup(16#ea, 16#b) -> {more, 16#0e, 16#0f}; -dec_huffman_lookup(16#ea, 16#c) -> {more, 16#0e, 16#18}; -dec_huffman_lookup(16#ea, 16#d) -> {more, 16#0e, 16#1f}; -dec_huffman_lookup(16#ea, 16#e) -> {more, 16#0e, 16#29}; -dec_huffman_lookup(16#ea, 16#f) -> {ok, 16#0e, 16#38}; -dec_huffman_lookup(16#eb, 16#0) -> {more, 16#0f, 16#02}; -dec_huffman_lookup(16#eb, 16#1) -> {more, 16#0f, 16#09}; -dec_huffman_lookup(16#eb, 16#2) -> {more, 16#0f, 16#17}; -dec_huffman_lookup(16#eb, 16#3) -> {ok, 16#0f, 16#28}; -dec_huffman_lookup(16#eb, 16#4) -> {more, 16#10, 16#02}; -dec_huffman_lookup(16#eb, 16#5) -> {more, 16#10, 16#09}; -dec_huffman_lookup(16#eb, 16#6) -> {more, 16#10, 16#17}; -dec_huffman_lookup(16#eb, 16#7) -> {ok, 16#10, 16#28}; -dec_huffman_lookup(16#eb, 16#8) -> {more, 16#11, 16#02}; -dec_huffman_lookup(16#eb, 16#9) -> {more, 16#11, 16#09}; -dec_huffman_lookup(16#eb, 16#a) -> {more, 16#11, 16#17}; -dec_huffman_lookup(16#eb, 16#b) -> {ok, 16#11, 16#28}; -dec_huffman_lookup(16#eb, 16#c) -> {more, 16#12, 16#02}; -dec_huffman_lookup(16#eb, 16#d) -> {more, 16#12, 16#09}; -dec_huffman_lookup(16#eb, 16#e) -> {more, 16#12, 16#17}; -dec_huffman_lookup(16#eb, 16#f) -> {ok, 16#12, 16#28}; -dec_huffman_lookup(16#ec, 16#0) -> {more, 16#0f, 16#03}; -dec_huffman_lookup(16#ec, 16#1) -> {more, 16#0f, 16#06}; -dec_huffman_lookup(16#ec, 16#2) -> {more, 16#0f, 16#0a}; -dec_huffman_lookup(16#ec, 16#3) -> {more, 16#0f, 16#0f}; -dec_huffman_lookup(16#ec, 16#4) -> {more, 16#0f, 16#18}; -dec_huffman_lookup(16#ec, 16#5) -> {more, 16#0f, 16#1f}; -dec_huffman_lookup(16#ec, 16#6) -> {more, 16#0f, 16#29}; -dec_huffman_lookup(16#ec, 16#7) -> {ok, 16#0f, 16#38}; -dec_huffman_lookup(16#ec, 16#8) -> {more, 16#10, 16#03}; -dec_huffman_lookup(16#ec, 16#9) -> {more, 16#10, 16#06}; -dec_huffman_lookup(16#ec, 16#a) -> {more, 16#10, 16#0a}; -dec_huffman_lookup(16#ec, 16#b) -> {more, 16#10, 16#0f}; -dec_huffman_lookup(16#ec, 16#c) -> {more, 16#10, 16#18}; -dec_huffman_lookup(16#ec, 16#d) -> {more, 16#10, 16#1f}; -dec_huffman_lookup(16#ec, 16#e) -> {more, 16#10, 16#29}; -dec_huffman_lookup(16#ec, 16#f) -> {ok, 16#10, 16#38}; -dec_huffman_lookup(16#ed, 16#0) -> {more, 16#11, 16#03}; -dec_huffman_lookup(16#ed, 16#1) -> {more, 16#11, 16#06}; -dec_huffman_lookup(16#ed, 16#2) -> {more, 16#11, 16#0a}; -dec_huffman_lookup(16#ed, 16#3) -> {more, 16#11, 16#0f}; -dec_huffman_lookup(16#ed, 16#4) -> {more, 16#11, 16#18}; -dec_huffman_lookup(16#ed, 16#5) -> {more, 16#11, 16#1f}; -dec_huffman_lookup(16#ed, 16#6) -> {more, 16#11, 16#29}; -dec_huffman_lookup(16#ed, 16#7) -> {ok, 16#11, 16#38}; -dec_huffman_lookup(16#ed, 16#8) -> {more, 16#12, 16#03}; -dec_huffman_lookup(16#ed, 16#9) -> {more, 16#12, 16#06}; -dec_huffman_lookup(16#ed, 16#a) -> {more, 16#12, 16#0a}; -dec_huffman_lookup(16#ed, 16#b) -> {more, 16#12, 16#0f}; -dec_huffman_lookup(16#ed, 16#c) -> {more, 16#12, 16#18}; -dec_huffman_lookup(16#ed, 16#d) -> {more, 16#12, 16#1f}; -dec_huffman_lookup(16#ed, 16#e) -> {more, 16#12, 16#29}; -dec_huffman_lookup(16#ed, 16#f) -> {ok, 16#12, 16#38}; -dec_huffman_lookup(16#ee, 16#0) -> {ok, 16#13, 16#00}; -dec_huffman_lookup(16#ee, 16#1) -> {ok, 16#14, 16#00}; -dec_huffman_lookup(16#ee, 16#2) -> {ok, 16#15, 16#00}; -dec_huffman_lookup(16#ee, 16#3) -> {ok, 16#17, 16#00}; -dec_huffman_lookup(16#ee, 16#4) -> {ok, 16#18, 16#00}; -dec_huffman_lookup(16#ee, 16#5) -> {ok, 16#19, 16#00}; -dec_huffman_lookup(16#ee, 16#6) -> {ok, 16#1a, 16#00}; -dec_huffman_lookup(16#ee, 16#7) -> {ok, 16#1b, 16#00}; -dec_huffman_lookup(16#ee, 16#8) -> {ok, 16#1c, 16#00}; -dec_huffman_lookup(16#ee, 16#9) -> {ok, 16#1d, 16#00}; -dec_huffman_lookup(16#ee, 16#a) -> {ok, 16#1e, 16#00}; -dec_huffman_lookup(16#ee, 16#b) -> {ok, 16#1f, 16#00}; -dec_huffman_lookup(16#ee, 16#c) -> {ok, 16#7f, 16#00}; -dec_huffman_lookup(16#ee, 16#d) -> {ok, 16#dc, 16#00}; -dec_huffman_lookup(16#ee, 16#e) -> {ok, 16#f9, 16#00}; -dec_huffman_lookup(16#ee, 16#f) -> {ok, undefined, 16#fd}; -dec_huffman_lookup(16#ef, 16#0) -> {more, 16#13, 16#01}; -dec_huffman_lookup(16#ef, 16#1) -> {ok, 16#13, 16#16}; -dec_huffman_lookup(16#ef, 16#2) -> {more, 16#14, 16#01}; -dec_huffman_lookup(16#ef, 16#3) -> {ok, 16#14, 16#16}; -dec_huffman_lookup(16#ef, 16#4) -> {more, 16#15, 16#01}; -dec_huffman_lookup(16#ef, 16#5) -> {ok, 16#15, 16#16}; -dec_huffman_lookup(16#ef, 16#6) -> {more, 16#17, 16#01}; -dec_huffman_lookup(16#ef, 16#7) -> {ok, 16#17, 16#16}; -dec_huffman_lookup(16#ef, 16#8) -> {more, 16#18, 16#01}; -dec_huffman_lookup(16#ef, 16#9) -> {ok, 16#18, 16#16}; -dec_huffman_lookup(16#ef, 16#a) -> {more, 16#19, 16#01}; -dec_huffman_lookup(16#ef, 16#b) -> {ok, 16#19, 16#16}; -dec_huffman_lookup(16#ef, 16#c) -> {more, 16#1a, 16#01}; -dec_huffman_lookup(16#ef, 16#d) -> {ok, 16#1a, 16#16}; -dec_huffman_lookup(16#ef, 16#e) -> {more, 16#1b, 16#01}; -dec_huffman_lookup(16#ef, 16#f) -> {ok, 16#1b, 16#16}; -dec_huffman_lookup(16#f0, 16#0) -> {more, 16#13, 16#02}; -dec_huffman_lookup(16#f0, 16#1) -> {more, 16#13, 16#09}; -dec_huffman_lookup(16#f0, 16#2) -> {more, 16#13, 16#17}; -dec_huffman_lookup(16#f0, 16#3) -> {ok, 16#13, 16#28}; -dec_huffman_lookup(16#f0, 16#4) -> {more, 16#14, 16#02}; -dec_huffman_lookup(16#f0, 16#5) -> {more, 16#14, 16#09}; -dec_huffman_lookup(16#f0, 16#6) -> {more, 16#14, 16#17}; -dec_huffman_lookup(16#f0, 16#7) -> {ok, 16#14, 16#28}; -dec_huffman_lookup(16#f0, 16#8) -> {more, 16#15, 16#02}; -dec_huffman_lookup(16#f0, 16#9) -> {more, 16#15, 16#09}; -dec_huffman_lookup(16#f0, 16#a) -> {more, 16#15, 16#17}; -dec_huffman_lookup(16#f0, 16#b) -> {ok, 16#15, 16#28}; -dec_huffman_lookup(16#f0, 16#c) -> {more, 16#17, 16#02}; -dec_huffman_lookup(16#f0, 16#d) -> {more, 16#17, 16#09}; -dec_huffman_lookup(16#f0, 16#e) -> {more, 16#17, 16#17}; -dec_huffman_lookup(16#f0, 16#f) -> {ok, 16#17, 16#28}; -dec_huffman_lookup(16#f1, 16#0) -> {more, 16#13, 16#03}; -dec_huffman_lookup(16#f1, 16#1) -> {more, 16#13, 16#06}; -dec_huffman_lookup(16#f1, 16#2) -> {more, 16#13, 16#0a}; -dec_huffman_lookup(16#f1, 16#3) -> {more, 16#13, 16#0f}; -dec_huffman_lookup(16#f1, 16#4) -> {more, 16#13, 16#18}; -dec_huffman_lookup(16#f1, 16#5) -> {more, 16#13, 16#1f}; -dec_huffman_lookup(16#f1, 16#6) -> {more, 16#13, 16#29}; -dec_huffman_lookup(16#f1, 16#7) -> {ok, 16#13, 16#38}; -dec_huffman_lookup(16#f1, 16#8) -> {more, 16#14, 16#03}; -dec_huffman_lookup(16#f1, 16#9) -> {more, 16#14, 16#06}; -dec_huffman_lookup(16#f1, 16#a) -> {more, 16#14, 16#0a}; -dec_huffman_lookup(16#f1, 16#b) -> {more, 16#14, 16#0f}; -dec_huffman_lookup(16#f1, 16#c) -> {more, 16#14, 16#18}; -dec_huffman_lookup(16#f1, 16#d) -> {more, 16#14, 16#1f}; -dec_huffman_lookup(16#f1, 16#e) -> {more, 16#14, 16#29}; -dec_huffman_lookup(16#f1, 16#f) -> {ok, 16#14, 16#38}; -dec_huffman_lookup(16#f2, 16#0) -> {more, 16#15, 16#03}; -dec_huffman_lookup(16#f2, 16#1) -> {more, 16#15, 16#06}; -dec_huffman_lookup(16#f2, 16#2) -> {more, 16#15, 16#0a}; -dec_huffman_lookup(16#f2, 16#3) -> {more, 16#15, 16#0f}; -dec_huffman_lookup(16#f2, 16#4) -> {more, 16#15, 16#18}; -dec_huffman_lookup(16#f2, 16#5) -> {more, 16#15, 16#1f}; -dec_huffman_lookup(16#f2, 16#6) -> {more, 16#15, 16#29}; -dec_huffman_lookup(16#f2, 16#7) -> {ok, 16#15, 16#38}; -dec_huffman_lookup(16#f2, 16#8) -> {more, 16#17, 16#03}; -dec_huffman_lookup(16#f2, 16#9) -> {more, 16#17, 16#06}; -dec_huffman_lookup(16#f2, 16#a) -> {more, 16#17, 16#0a}; -dec_huffman_lookup(16#f2, 16#b) -> {more, 16#17, 16#0f}; -dec_huffman_lookup(16#f2, 16#c) -> {more, 16#17, 16#18}; -dec_huffman_lookup(16#f2, 16#d) -> {more, 16#17, 16#1f}; -dec_huffman_lookup(16#f2, 16#e) -> {more, 16#17, 16#29}; -dec_huffman_lookup(16#f2, 16#f) -> {ok, 16#17, 16#38}; -dec_huffman_lookup(16#f3, 16#0) -> {more, 16#18, 16#02}; -dec_huffman_lookup(16#f3, 16#1) -> {more, 16#18, 16#09}; -dec_huffman_lookup(16#f3, 16#2) -> {more, 16#18, 16#17}; -dec_huffman_lookup(16#f3, 16#3) -> {ok, 16#18, 16#28}; -dec_huffman_lookup(16#f3, 16#4) -> {more, 16#19, 16#02}; -dec_huffman_lookup(16#f3, 16#5) -> {more, 16#19, 16#09}; -dec_huffman_lookup(16#f3, 16#6) -> {more, 16#19, 16#17}; -dec_huffman_lookup(16#f3, 16#7) -> {ok, 16#19, 16#28}; -dec_huffman_lookup(16#f3, 16#8) -> {more, 16#1a, 16#02}; -dec_huffman_lookup(16#f3, 16#9) -> {more, 16#1a, 16#09}; -dec_huffman_lookup(16#f3, 16#a) -> {more, 16#1a, 16#17}; -dec_huffman_lookup(16#f3, 16#b) -> {ok, 16#1a, 16#28}; -dec_huffman_lookup(16#f3, 16#c) -> {more, 16#1b, 16#02}; -dec_huffman_lookup(16#f3, 16#d) -> {more, 16#1b, 16#09}; -dec_huffman_lookup(16#f3, 16#e) -> {more, 16#1b, 16#17}; -dec_huffman_lookup(16#f3, 16#f) -> {ok, 16#1b, 16#28}; -dec_huffman_lookup(16#f4, 16#0) -> {more, 16#18, 16#03}; -dec_huffman_lookup(16#f4, 16#1) -> {more, 16#18, 16#06}; -dec_huffman_lookup(16#f4, 16#2) -> {more, 16#18, 16#0a}; -dec_huffman_lookup(16#f4, 16#3) -> {more, 16#18, 16#0f}; -dec_huffman_lookup(16#f4, 16#4) -> {more, 16#18, 16#18}; -dec_huffman_lookup(16#f4, 16#5) -> {more, 16#18, 16#1f}; -dec_huffman_lookup(16#f4, 16#6) -> {more, 16#18, 16#29}; -dec_huffman_lookup(16#f4, 16#7) -> {ok, 16#18, 16#38}; -dec_huffman_lookup(16#f4, 16#8) -> {more, 16#19, 16#03}; -dec_huffman_lookup(16#f4, 16#9) -> {more, 16#19, 16#06}; -dec_huffman_lookup(16#f4, 16#a) -> {more, 16#19, 16#0a}; -dec_huffman_lookup(16#f4, 16#b) -> {more, 16#19, 16#0f}; -dec_huffman_lookup(16#f4, 16#c) -> {more, 16#19, 16#18}; -dec_huffman_lookup(16#f4, 16#d) -> {more, 16#19, 16#1f}; -dec_huffman_lookup(16#f4, 16#e) -> {more, 16#19, 16#29}; -dec_huffman_lookup(16#f4, 16#f) -> {ok, 16#19, 16#38}; -dec_huffman_lookup(16#f5, 16#0) -> {more, 16#1a, 16#03}; -dec_huffman_lookup(16#f5, 16#1) -> {more, 16#1a, 16#06}; -dec_huffman_lookup(16#f5, 16#2) -> {more, 16#1a, 16#0a}; -dec_huffman_lookup(16#f5, 16#3) -> {more, 16#1a, 16#0f}; -dec_huffman_lookup(16#f5, 16#4) -> {more, 16#1a, 16#18}; -dec_huffman_lookup(16#f5, 16#5) -> {more, 16#1a, 16#1f}; -dec_huffman_lookup(16#f5, 16#6) -> {more, 16#1a, 16#29}; -dec_huffman_lookup(16#f5, 16#7) -> {ok, 16#1a, 16#38}; -dec_huffman_lookup(16#f5, 16#8) -> {more, 16#1b, 16#03}; -dec_huffman_lookup(16#f5, 16#9) -> {more, 16#1b, 16#06}; -dec_huffman_lookup(16#f5, 16#a) -> {more, 16#1b, 16#0a}; -dec_huffman_lookup(16#f5, 16#b) -> {more, 16#1b, 16#0f}; -dec_huffman_lookup(16#f5, 16#c) -> {more, 16#1b, 16#18}; -dec_huffman_lookup(16#f5, 16#d) -> {more, 16#1b, 16#1f}; -dec_huffman_lookup(16#f5, 16#e) -> {more, 16#1b, 16#29}; -dec_huffman_lookup(16#f5, 16#f) -> {ok, 16#1b, 16#38}; -dec_huffman_lookup(16#f6, 16#0) -> {more, 16#1c, 16#01}; -dec_huffman_lookup(16#f6, 16#1) -> {ok, 16#1c, 16#16}; -dec_huffman_lookup(16#f6, 16#2) -> {more, 16#1d, 16#01}; -dec_huffman_lookup(16#f6, 16#3) -> {ok, 16#1d, 16#16}; -dec_huffman_lookup(16#f6, 16#4) -> {more, 16#1e, 16#01}; -dec_huffman_lookup(16#f6, 16#5) -> {ok, 16#1e, 16#16}; -dec_huffman_lookup(16#f6, 16#6) -> {more, 16#1f, 16#01}; -dec_huffman_lookup(16#f6, 16#7) -> {ok, 16#1f, 16#16}; -dec_huffman_lookup(16#f6, 16#8) -> {more, 16#7f, 16#01}; -dec_huffman_lookup(16#f6, 16#9) -> {ok, 16#7f, 16#16}; -dec_huffman_lookup(16#f6, 16#a) -> {more, 16#dc, 16#01}; -dec_huffman_lookup(16#f6, 16#b) -> {ok, 16#dc, 16#16}; -dec_huffman_lookup(16#f6, 16#c) -> {more, 16#f9, 16#01}; -dec_huffman_lookup(16#f6, 16#d) -> {ok, 16#f9, 16#16}; -dec_huffman_lookup(16#f6, 16#e) -> {more, undefined, 16#fe}; -dec_huffman_lookup(16#f6, 16#f) -> {ok, undefined, 16#ff}; -dec_huffman_lookup(16#f7, 16#0) -> {more, 16#1c, 16#02}; -dec_huffman_lookup(16#f7, 16#1) -> {more, 16#1c, 16#09}; -dec_huffman_lookup(16#f7, 16#2) -> {more, 16#1c, 16#17}; -dec_huffman_lookup(16#f7, 16#3) -> {ok, 16#1c, 16#28}; -dec_huffman_lookup(16#f7, 16#4) -> {more, 16#1d, 16#02}; -dec_huffman_lookup(16#f7, 16#5) -> {more, 16#1d, 16#09}; -dec_huffman_lookup(16#f7, 16#6) -> {more, 16#1d, 16#17}; -dec_huffman_lookup(16#f7, 16#7) -> {ok, 16#1d, 16#28}; -dec_huffman_lookup(16#f7, 16#8) -> {more, 16#1e, 16#02}; -dec_huffman_lookup(16#f7, 16#9) -> {more, 16#1e, 16#09}; -dec_huffman_lookup(16#f7, 16#a) -> {more, 16#1e, 16#17}; -dec_huffman_lookup(16#f7, 16#b) -> {ok, 16#1e, 16#28}; -dec_huffman_lookup(16#f7, 16#c) -> {more, 16#1f, 16#02}; -dec_huffman_lookup(16#f7, 16#d) -> {more, 16#1f, 16#09}; -dec_huffman_lookup(16#f7, 16#e) -> {more, 16#1f, 16#17}; -dec_huffman_lookup(16#f7, 16#f) -> {ok, 16#1f, 16#28}; -dec_huffman_lookup(16#f8, 16#0) -> {more, 16#1c, 16#03}; -dec_huffman_lookup(16#f8, 16#1) -> {more, 16#1c, 16#06}; -dec_huffman_lookup(16#f8, 16#2) -> {more, 16#1c, 16#0a}; -dec_huffman_lookup(16#f8, 16#3) -> {more, 16#1c, 16#0f}; -dec_huffman_lookup(16#f8, 16#4) -> {more, 16#1c, 16#18}; -dec_huffman_lookup(16#f8, 16#5) -> {more, 16#1c, 16#1f}; -dec_huffman_lookup(16#f8, 16#6) -> {more, 16#1c, 16#29}; -dec_huffman_lookup(16#f8, 16#7) -> {ok, 16#1c, 16#38}; -dec_huffman_lookup(16#f8, 16#8) -> {more, 16#1d, 16#03}; -dec_huffman_lookup(16#f8, 16#9) -> {more, 16#1d, 16#06}; -dec_huffman_lookup(16#f8, 16#a) -> {more, 16#1d, 16#0a}; -dec_huffman_lookup(16#f8, 16#b) -> {more, 16#1d, 16#0f}; -dec_huffman_lookup(16#f8, 16#c) -> {more, 16#1d, 16#18}; -dec_huffman_lookup(16#f8, 16#d) -> {more, 16#1d, 16#1f}; -dec_huffman_lookup(16#f8, 16#e) -> {more, 16#1d, 16#29}; -dec_huffman_lookup(16#f8, 16#f) -> {ok, 16#1d, 16#38}; -dec_huffman_lookup(16#f9, 16#0) -> {more, 16#1e, 16#03}; -dec_huffman_lookup(16#f9, 16#1) -> {more, 16#1e, 16#06}; -dec_huffman_lookup(16#f9, 16#2) -> {more, 16#1e, 16#0a}; -dec_huffman_lookup(16#f9, 16#3) -> {more, 16#1e, 16#0f}; -dec_huffman_lookup(16#f9, 16#4) -> {more, 16#1e, 16#18}; -dec_huffman_lookup(16#f9, 16#5) -> {more, 16#1e, 16#1f}; -dec_huffman_lookup(16#f9, 16#6) -> {more, 16#1e, 16#29}; -dec_huffman_lookup(16#f9, 16#7) -> {ok, 16#1e, 16#38}; -dec_huffman_lookup(16#f9, 16#8) -> {more, 16#1f, 16#03}; -dec_huffman_lookup(16#f9, 16#9) -> {more, 16#1f, 16#06}; -dec_huffman_lookup(16#f9, 16#a) -> {more, 16#1f, 16#0a}; -dec_huffman_lookup(16#f9, 16#b) -> {more, 16#1f, 16#0f}; -dec_huffman_lookup(16#f9, 16#c) -> {more, 16#1f, 16#18}; -dec_huffman_lookup(16#f9, 16#d) -> {more, 16#1f, 16#1f}; -dec_huffman_lookup(16#f9, 16#e) -> {more, 16#1f, 16#29}; -dec_huffman_lookup(16#f9, 16#f) -> {ok, 16#1f, 16#38}; -dec_huffman_lookup(16#fa, 16#0) -> {more, 16#7f, 16#02}; -dec_huffman_lookup(16#fa, 16#1) -> {more, 16#7f, 16#09}; -dec_huffman_lookup(16#fa, 16#2) -> {more, 16#7f, 16#17}; -dec_huffman_lookup(16#fa, 16#3) -> {ok, 16#7f, 16#28}; -dec_huffman_lookup(16#fa, 16#4) -> {more, 16#dc, 16#02}; -dec_huffman_lookup(16#fa, 16#5) -> {more, 16#dc, 16#09}; -dec_huffman_lookup(16#fa, 16#6) -> {more, 16#dc, 16#17}; -dec_huffman_lookup(16#fa, 16#7) -> {ok, 16#dc, 16#28}; -dec_huffman_lookup(16#fa, 16#8) -> {more, 16#f9, 16#02}; -dec_huffman_lookup(16#fa, 16#9) -> {more, 16#f9, 16#09}; -dec_huffman_lookup(16#fa, 16#a) -> {more, 16#f9, 16#17}; -dec_huffman_lookup(16#fa, 16#b) -> {ok, 16#f9, 16#28}; -dec_huffman_lookup(16#fa, 16#c) -> {ok, 16#0a, 16#00}; -dec_huffman_lookup(16#fa, 16#d) -> {ok, 16#0d, 16#00}; -dec_huffman_lookup(16#fa, 16#e) -> {ok, 16#16, 16#00}; -dec_huffman_lookup(16#fa, 16#f) -> error; -dec_huffman_lookup(16#fb, 16#0) -> {more, 16#7f, 16#03}; -dec_huffman_lookup(16#fb, 16#1) -> {more, 16#7f, 16#06}; -dec_huffman_lookup(16#fb, 16#2) -> {more, 16#7f, 16#0a}; -dec_huffman_lookup(16#fb, 16#3) -> {more, 16#7f, 16#0f}; -dec_huffman_lookup(16#fb, 16#4) -> {more, 16#7f, 16#18}; -dec_huffman_lookup(16#fb, 16#5) -> {more, 16#7f, 16#1f}; -dec_huffman_lookup(16#fb, 16#6) -> {more, 16#7f, 16#29}; -dec_huffman_lookup(16#fb, 16#7) -> {ok, 16#7f, 16#38}; -dec_huffman_lookup(16#fb, 16#8) -> {more, 16#dc, 16#03}; -dec_huffman_lookup(16#fb, 16#9) -> {more, 16#dc, 16#06}; -dec_huffman_lookup(16#fb, 16#a) -> {more, 16#dc, 16#0a}; -dec_huffman_lookup(16#fb, 16#b) -> {more, 16#dc, 16#0f}; -dec_huffman_lookup(16#fb, 16#c) -> {more, 16#dc, 16#18}; -dec_huffman_lookup(16#fb, 16#d) -> {more, 16#dc, 16#1f}; -dec_huffman_lookup(16#fb, 16#e) -> {more, 16#dc, 16#29}; -dec_huffman_lookup(16#fb, 16#f) -> {ok, 16#dc, 16#38}; -dec_huffman_lookup(16#fc, 16#0) -> {more, 16#f9, 16#03}; -dec_huffman_lookup(16#fc, 16#1) -> {more, 16#f9, 16#06}; -dec_huffman_lookup(16#fc, 16#2) -> {more, 16#f9, 16#0a}; -dec_huffman_lookup(16#fc, 16#3) -> {more, 16#f9, 16#0f}; -dec_huffman_lookup(16#fc, 16#4) -> {more, 16#f9, 16#18}; -dec_huffman_lookup(16#fc, 16#5) -> {more, 16#f9, 16#1f}; -dec_huffman_lookup(16#fc, 16#6) -> {more, 16#f9, 16#29}; -dec_huffman_lookup(16#fc, 16#7) -> {ok, 16#f9, 16#38}; -dec_huffman_lookup(16#fc, 16#8) -> {more, 16#0a, 16#01}; -dec_huffman_lookup(16#fc, 16#9) -> {ok, 16#0a, 16#16}; -dec_huffman_lookup(16#fc, 16#a) -> {more, 16#0d, 16#01}; -dec_huffman_lookup(16#fc, 16#b) -> {ok, 16#0d, 16#16}; -dec_huffman_lookup(16#fc, 16#c) -> {more, 16#16, 16#01}; -dec_huffman_lookup(16#fc, 16#d) -> {ok, 16#16, 16#16}; -dec_huffman_lookup(16#fc, 16#e) -> error; -dec_huffman_lookup(16#fc, 16#f) -> error; -dec_huffman_lookup(16#fd, 16#0) -> {more, 16#0a, 16#02}; -dec_huffman_lookup(16#fd, 16#1) -> {more, 16#0a, 16#09}; -dec_huffman_lookup(16#fd, 16#2) -> {more, 16#0a, 16#17}; -dec_huffman_lookup(16#fd, 16#3) -> {ok, 16#0a, 16#28}; -dec_huffman_lookup(16#fd, 16#4) -> {more, 16#0d, 16#02}; -dec_huffman_lookup(16#fd, 16#5) -> {more, 16#0d, 16#09}; -dec_huffman_lookup(16#fd, 16#6) -> {more, 16#0d, 16#17}; -dec_huffman_lookup(16#fd, 16#7) -> {ok, 16#0d, 16#28}; -dec_huffman_lookup(16#fd, 16#8) -> {more, 16#16, 16#02}; -dec_huffman_lookup(16#fd, 16#9) -> {more, 16#16, 16#09}; -dec_huffman_lookup(16#fd, 16#a) -> {more, 16#16, 16#17}; -dec_huffman_lookup(16#fd, 16#b) -> {ok, 16#16, 16#28}; -dec_huffman_lookup(16#fd, 16#c) -> error; -dec_huffman_lookup(16#fd, 16#d) -> error; -dec_huffman_lookup(16#fd, 16#e) -> error; -dec_huffman_lookup(16#fd, 16#f) -> error; -dec_huffman_lookup(16#fe, 16#0) -> {more, 16#0a, 16#03}; -dec_huffman_lookup(16#fe, 16#1) -> {more, 16#0a, 16#06}; -dec_huffman_lookup(16#fe, 16#2) -> {more, 16#0a, 16#0a}; -dec_huffman_lookup(16#fe, 16#3) -> {more, 16#0a, 16#0f}; -dec_huffman_lookup(16#fe, 16#4) -> {more, 16#0a, 16#18}; -dec_huffman_lookup(16#fe, 16#5) -> {more, 16#0a, 16#1f}; -dec_huffman_lookup(16#fe, 16#6) -> {more, 16#0a, 16#29}; -dec_huffman_lookup(16#fe, 16#7) -> {ok, 16#0a, 16#38}; -dec_huffman_lookup(16#fe, 16#8) -> {more, 16#0d, 16#03}; -dec_huffman_lookup(16#fe, 16#9) -> {more, 16#0d, 16#06}; -dec_huffman_lookup(16#fe, 16#a) -> {more, 16#0d, 16#0a}; -dec_huffman_lookup(16#fe, 16#b) -> {more, 16#0d, 16#0f}; -dec_huffman_lookup(16#fe, 16#c) -> {more, 16#0d, 16#18}; -dec_huffman_lookup(16#fe, 16#d) -> {more, 16#0d, 16#1f}; -dec_huffman_lookup(16#fe, 16#e) -> {more, 16#0d, 16#29}; -dec_huffman_lookup(16#fe, 16#f) -> {ok, 16#0d, 16#38}; -dec_huffman_lookup(16#ff, 16#0) -> {more, 16#16, 16#03}; -dec_huffman_lookup(16#ff, 16#1) -> {more, 16#16, 16#06}; -dec_huffman_lookup(16#ff, 16#2) -> {more, 16#16, 16#0a}; -dec_huffman_lookup(16#ff, 16#3) -> {more, 16#16, 16#0f}; -dec_huffman_lookup(16#ff, 16#4) -> {more, 16#16, 16#18}; -dec_huffman_lookup(16#ff, 16#5) -> {more, 16#16, 16#1f}; -dec_huffman_lookup(16#ff, 16#6) -> {more, 16#16, 16#29}; -dec_huffman_lookup(16#ff, 16#7) -> {ok, 16#16, 16#38}; -dec_huffman_lookup(16#ff, 16#8) -> error; -dec_huffman_lookup(16#ff, 16#9) -> error; -dec_huffman_lookup(16#ff, 16#a) -> error; -dec_huffman_lookup(16#ff, 16#b) -> error; -dec_huffman_lookup(16#ff, 16#c) -> error; -dec_huffman_lookup(16#ff, 16#d) -> error; -dec_huffman_lookup(16#ff, 16#e) -> error; -dec_huffman_lookup(16#ff, 16#f) -> error. diff --git a/src/hackney_http2.erl b/src/hackney_http2.erl deleted file mode 100644 index e7b07c8a..00000000 --- a/src/hackney_http2.erl +++ /dev/null @@ -1,468 +0,0 @@ -%%% -*- erlang -*- -%%% -%%% Based on cow_http2 from cowlib (https://github.com/ninenines/cowlib) -%%% Modified for hackney. -%%% -%% Copyright (c) 2015-2023, Loïc Hoguin -%% Copyright (c) 2026, Benoit Chesneau -%% -%% Permission to use, copy, modify, and/or distribute this software for any -%% purpose with or without fee is hereby granted, provided that the above -%% copyright notice and this permission notice appear in all copies. -%% -%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - --module(hackney_http2). - -%% Parsing. --export([parse_sequence/1]). --export([parse/1]). --export([parse/2]). --export([parse_settings_payload/1]). - -%% Building. --export([data/3]). --export([data_header/3]). --export([headers/3]). --export([priority/4]). --export([rst_stream/2]). --export([settings/1]). --export([settings_payload/1]). --export([settings_ack/0]). --export([push_promise/3]). --export([ping/1]). --export([ping_ack/1]). --export([goaway/3]). --export([window_update/1]). --export([window_update/2]). - --type streamid() :: pos_integer(). --export_type([streamid/0]). - --type fin() :: fin | nofin. --export_type([fin/0]). - --type head_fin() :: head_fin | head_nofin. --export_type([head_fin/0]). - --type exclusive() :: exclusive | shared. --type weight() :: 1..256. --type settings() :: map(). - --type error() :: no_error - | protocol_error - | internal_error - | flow_control_error - | settings_timeout - | stream_closed - | frame_size_error - | refused_stream - | cancel - | compression_error - | connect_error - | enhance_your_calm - | inadequate_security - | http_1_1_required - | unknown_error. --export_type([error/0]). - --type frame() :: {data, streamid(), fin(), binary()} - | {headers, streamid(), fin(), head_fin(), binary()} - | {headers, streamid(), fin(), head_fin(), exclusive(), streamid(), weight(), binary()} - | {priority, streamid(), exclusive(), streamid(), weight()} - | {rst_stream, streamid(), error()} - | {settings, settings()} - | settings_ack - | {push_promise, streamid(), head_fin(), streamid(), binary()} - | {ping, integer()} - | {ping_ack, integer()} - | {goaway, streamid(), error(), binary()} - | {window_update, non_neg_integer()} - | {window_update, streamid(), non_neg_integer()} - | {continuation, streamid(), head_fin(), binary()}. --export_type([frame/0]). - -%% Parsing. - --spec parse_sequence(binary()) - -> {ok, binary()} | more | {connection_error, error(), atom()}. -parse_sequence(<<"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", Rest/bits>>) -> - {ok, Rest}; -parse_sequence(Data) when byte_size(Data) >= 24 -> - {connection_error, protocol_error, - 'The connection preface was invalid. (RFC7540 3.5)'}; -parse_sequence(Data) -> - Len = byte_size(Data), - <> = <<"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n">>, - case Data of - Preface -> - more; - _ -> - {connection_error, protocol_error, - 'The connection preface was invalid. (RFC7540 3.5)'} - end. - -parse(<< Len:24, _/bits >>, MaxFrameSize) when Len > MaxFrameSize -> - {connection_error, frame_size_error, 'The frame size exceeded SETTINGS_MAX_FRAME_SIZE. (RFC7540 4.2)'}; -parse(Data, _) -> - parse(Data). - -%% -%% DATA frames. -%% -parse(<< _:24, 0:8, _:9, 0:31, _/bits >>) -> - {connection_error, protocol_error, 'DATA frames MUST be associated with a stream. (RFC7540 6.1)'}; -parse(<< 0:24, 0:8, _:4, 1:1, _:35, _/bits >>) -> - {connection_error, frame_size_error, 'DATA frames with padding flag MUST have a length > 0. (RFC7540 6.1)'}; -parse(<< Len0:24, 0:8, _:4, 1:1, _:35, PadLen:8, _/bits >>) when PadLen >= Len0 -> - {connection_error, protocol_error, 'Length of padding MUST be less than length of payload. (RFC7540 6.1)'}; -%% No padding. -parse(<< Len:24, 0:8, _:4, 0:1, _:2, FlagEndStream:1, _:1, StreamID:31, Data:Len/binary, Rest/bits >>) -> - {ok, {data, StreamID, parse_fin(FlagEndStream), Data}, Rest}; -%% Padding. -parse(<< Len0:24, 0:8, _:4, 1:1, _:2, FlagEndStream:1, _:1, StreamID:31, PadLen:8, Rest0/bits >>) - when byte_size(Rest0) >= Len0 - 1 -> - Len = Len0 - PadLen - 1, - case Rest0 of - << Data:Len/binary, 0:PadLen/unit:8, Rest/bits >> -> - {ok, {data, StreamID, parse_fin(FlagEndStream), Data}, Rest}; - _ -> - {connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.1)'} - end; -%% -%% HEADERS frames. -%% -parse(<< _:24, 1:8, _:9, 0:31, _/bits >>) -> - {connection_error, protocol_error, 'HEADERS frames MUST be associated with a stream. (RFC7540 6.2)'}; -parse(<< 0:24, 1:8, _:4, 1:1, _:35, _/bits >>) -> - {connection_error, frame_size_error, 'HEADERS frames with padding flag MUST have a length > 0. (RFC7540 6.1)'}; -parse(<< Len:24, 1:8, _:2, 1:1, _:37, _/bits >>) when Len < 5 -> - {connection_error, frame_size_error, 'HEADERS frames with priority flag MUST have a length >= 5. (RFC7540 6.1)'}; -parse(<< Len:24, 1:8, _:2, 1:1, _:1, 1:1, _:35, _/bits >>) when Len < 6 -> - {connection_error, frame_size_error, 'HEADERS frames with padding and priority flags MUST have a length >= 6. (RFC7540 6.1)'}; -parse(<< Len0:24, 1:8, _:4, 1:1, _:35, PadLen:8, _/bits >>) when PadLen >= Len0 -> - {connection_error, protocol_error, 'Length of padding MUST be less than length of payload. (RFC7540 6.2)'}; -parse(<< Len0:24, 1:8, _:2, 1:1, _:1, 1:1, _:35, PadLen:8, _/bits >>) when PadLen >= Len0 - 5 -> - {connection_error, protocol_error, 'Length of padding MUST be less than length of payload. (RFC7540 6.2)'}; -%% No padding, no priority. -parse(<< Len:24, 1:8, _:2, 0:1, _:1, 0:1, FlagEndHeaders:1, _:1, FlagEndStream:1, _:1, StreamID:31, - HeaderBlockFragment:Len/binary, Rest/bits >>) -> - {ok, {headers, StreamID, parse_fin(FlagEndStream), parse_head_fin(FlagEndHeaders), HeaderBlockFragment}, Rest}; -%% Padding, no priority. -parse(<< Len0:24, 1:8, _:2, 0:1, _:1, 1:1, FlagEndHeaders:1, _:1, FlagEndStream:1, _:1, StreamID:31, - PadLen:8, Rest0/bits >>) when byte_size(Rest0) >= Len0 - 1 -> - Len = Len0 - PadLen - 1, - case Rest0 of - << HeaderBlockFragment:Len/binary, 0:PadLen/unit:8, Rest/bits >> -> - {ok, {headers, StreamID, parse_fin(FlagEndStream), parse_head_fin(FlagEndHeaders), HeaderBlockFragment}, Rest}; - _ -> - {connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.2)'} - end; -%% No padding, priority. -parse(<< _:24, 1:8, _:2, 1:1, _:1, 0:1, _:4, StreamID:31, _:1, StreamID:31, _/bits >>) -> - {connection_error, protocol_error, - 'HEADERS frames cannot define a stream that depends on itself. (RFC7540 5.3.1)'}; -parse(<< Len0:24, 1:8, _:2, 1:1, _:1, 0:1, FlagEndHeaders:1, _:1, FlagEndStream:1, _:1, StreamID:31, - E:1, DepStreamID:31, Weight:8, Rest0/bits >>) when byte_size(Rest0) >= Len0 - 5 -> - Len = Len0 - 5, - << HeaderBlockFragment:Len/binary, Rest/bits >> = Rest0, - {ok, {headers, StreamID, parse_fin(FlagEndStream), parse_head_fin(FlagEndHeaders), - parse_exclusive(E), DepStreamID, Weight + 1, HeaderBlockFragment}, Rest}; -%% Padding, priority. -parse(<< _:24, 1:8, _:2, 1:1, _:1, 1:1, _:4, StreamID:31, _:9, StreamID:31, _/bits >>) -> - {connection_error, protocol_error, - 'HEADERS frames cannot define a stream that depends on itself. (RFC7540 5.3.1)'}; -parse(<< Len0:24, 1:8, _:2, 1:1, _:1, 1:1, FlagEndHeaders:1, _:1, FlagEndStream:1, _:1, StreamID:31, - PadLen:8, E:1, DepStreamID:31, Weight:8, Rest0/bits >>) when byte_size(Rest0) >= Len0 - 6 -> - Len = Len0 - PadLen - 6, - case Rest0 of - << HeaderBlockFragment:Len/binary, 0:PadLen/unit:8, Rest/bits >> -> - {ok, {headers, StreamID, parse_fin(FlagEndStream), parse_head_fin(FlagEndHeaders), - parse_exclusive(E), DepStreamID, Weight + 1, HeaderBlockFragment}, Rest}; - _ -> - {connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.2)'} - end; -%% -%% PRIORITY frames. -%% -parse(<< 5:24, 2:8, _:9, 0:31, _/bits >>) -> - {connection_error, protocol_error, 'PRIORITY frames MUST be associated with a stream. (RFC7540 6.3)'}; -parse(<< 5:24, 2:8, _:9, StreamID:31, _:1, StreamID:31, _:8, Rest/bits >>) -> - {stream_error, StreamID, protocol_error, - 'PRIORITY frames cannot make a stream depend on itself. (RFC7540 5.3.1)', Rest}; -parse(<< 5:24, 2:8, _:9, StreamID:31, E:1, DepStreamID:31, Weight:8, Rest/bits >>) -> - {ok, {priority, StreamID, parse_exclusive(E), DepStreamID, Weight + 1}, Rest}; -%% @todo Figure out how to best deal with non-fatal frame size errors; if we have everything -%% then OK if not we might want to inform the caller how much he should expect so that it can -%% decide if it should just close the connection -parse(<< BadLen:24, 2:8, _:9, StreamID:31, _:BadLen/binary, Rest/bits >>) -> - {stream_error, StreamID, frame_size_error, 'PRIORITY frames MUST be 5 bytes wide. (RFC7540 6.3)', Rest}; -%% -%% RST_STREAM frames. -%% -parse(<< 4:24, 3:8, _:9, 0:31, _/bits >>) -> - {connection_error, protocol_error, 'RST_STREAM frames MUST be associated with a stream. (RFC7540 6.4)'}; -parse(<< 4:24, 3:8, _:9, StreamID:31, ErrorCode:32, Rest/bits >>) -> - {ok, {rst_stream, StreamID, parse_error_code(ErrorCode)}, Rest}; -parse(<< BadLen:24, 3:8, _:9, _:31, _/bits >>) when BadLen =/= 4 -> - {connection_error, frame_size_error, 'RST_STREAM frames MUST be 4 bytes wide. (RFC7540 6.4)'}; -%% -%% SETTINGS frames. -%% -parse(<< 0:24, 4:8, _:7, 1:1, _:1, 0:31, Rest/bits >>) -> - {ok, settings_ack, Rest}; -parse(<< _:24, 4:8, _:7, 1:1, _:1, 0:31, _/bits >>) -> - {connection_error, frame_size_error, 'SETTINGS frames with the ACK flag set MUST have a length of 0. (RFC7540 6.5)'}; -parse(<< Len:24, 4:8, _:7, 0:1, _:1, 0:31, _/bits >>) when Len rem 6 =/= 0 -> - {connection_error, frame_size_error, 'SETTINGS frames MUST have a length multiple of 6. (RFC7540 6.5)'}; -parse(<< Len:24, 4:8, _:7, 0:1, _:1, 0:31, Rest/bits >>) when byte_size(Rest) >= Len -> - parse_settings_payload(Rest, Len, #{}); -parse(<< _:24, 4:8, _:8, _:1, StreamID:31, _/bits >>) when StreamID =/= 0 -> - {connection_error, protocol_error, 'SETTINGS frames MUST NOT be associated with a stream. (RFC7540 6.5)'}; -%% -%% PUSH_PROMISE frames. -%% -parse(<< Len:24, 5:8, _:40, _/bits >>) when Len < 4 -> - {connection_error, frame_size_error, 'PUSH_PROMISE frames MUST have a length >= 4. (RFC7540 4.2, RFC7540 6.6)'}; -parse(<< Len:24, 5:8, _:4, 1:1, _:35, _/bits >>) when Len < 5 -> - {connection_error, frame_size_error, 'PUSH_PROMISE frames with padding flag MUST have a length >= 5. (RFC7540 4.2, RFC7540 6.6)'}; -parse(<< _:24, 5:8, _:9, 0:31, _/bits >>) -> - {connection_error, protocol_error, 'PUSH_PROMISE frames MUST be associated with a stream. (RFC7540 6.6)'}; -parse(<< Len0:24, 5:8, _:4, 1:1, _:35, PadLen:8, _/bits >>) when PadLen >= Len0 - 4 -> - {connection_error, protocol_error, 'Length of padding MUST be less than length of payload. (RFC7540 6.6)'}; -parse(<< Len0:24, 5:8, _:4, 0:1, FlagEndHeaders:1, _:3, StreamID:31, _:1, PromisedStreamID:31, Rest0/bits >>) - when byte_size(Rest0) >= Len0 - 4 -> - Len = Len0 - 4, - << HeaderBlockFragment:Len/binary, Rest/bits >> = Rest0, - {ok, {push_promise, StreamID, parse_head_fin(FlagEndHeaders), PromisedStreamID, HeaderBlockFragment}, Rest}; -parse(<< Len0:24, 5:8, _:4, 1:1, FlagEndHeaders:1, _:2, StreamID:31, PadLen:8, _:1, PromisedStreamID:31, Rest0/bits >>) - when byte_size(Rest0) >= Len0 - 5 -> - Len = Len0 - 5, - case Rest0 of - << HeaderBlockFragment:Len/binary, 0:PadLen/unit:8, Rest/bits >> -> - {ok, {push_promise, StreamID, parse_head_fin(FlagEndHeaders), PromisedStreamID, HeaderBlockFragment}, Rest}; - _ -> - {connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.6)'} - end; -%% -%% PING frames. -%% -parse(<< 8:24, 6:8, _:7, 1:1, _:1, 0:31, Opaque:64, Rest/bits >>) -> - {ok, {ping_ack, Opaque}, Rest}; -parse(<< 8:24, 6:8, _:7, 0:1, _:1, 0:31, Opaque:64, Rest/bits >>) -> - {ok, {ping, Opaque}, Rest}; -parse(<< 8:24, 6:8, _:104, _/bits >>) -> - {connection_error, protocol_error, 'PING frames MUST NOT be associated with a stream. (RFC7540 6.7)'}; -parse(<< Len:24, 6:8, _/bits >>) when Len =/= 8 -> - {connection_error, frame_size_error, 'PING frames MUST be 8 bytes wide. (RFC7540 6.7)'}; -%% -%% GOAWAY frames. -%% -parse(<< Len0:24, 7:8, _:9, 0:31, _:1, LastStreamID:31, ErrorCode:32, Rest0/bits >>) when byte_size(Rest0) >= Len0 - 8 -> - Len = Len0 - 8, - << DebugData:Len/binary, Rest/bits >> = Rest0, - {ok, {goaway, LastStreamID, parse_error_code(ErrorCode), DebugData}, Rest}; -parse(<< Len:24, 7:8, _:40, _/bits >>) when Len < 8 -> - {connection_error, frame_size_error, 'GOAWAY frames MUST have a length >= 8. (RFC7540 4.2, RFC7540 6.8)'}; -parse(<< _:24, 7:8, _:40, _/bits >>) -> - {connection_error, protocol_error, 'GOAWAY frames MUST NOT be associated with a stream. (RFC7540 6.8)'}; -%% -%% WINDOW_UPDATE frames. -%% -parse(<< 4:24, 8:8, _:9, 0:31, _:1, 0:31, _/bits >>) -> - {connection_error, protocol_error, 'WINDOW_UPDATE frames MUST have a non-zero increment. (RFC7540 6.9)'}; -parse(<< 4:24, 8:8, _:9, 0:31, _:1, Increment:31, Rest/bits >>) -> - {ok, {window_update, Increment}, Rest}; -parse(<< 4:24, 8:8, _:9, StreamID:31, _:1, 0:31, Rest/bits >>) -> - {stream_error, StreamID, protocol_error, 'WINDOW_UPDATE frames MUST have a non-zero increment. (RFC7540 6.9)', Rest}; -parse(<< 4:24, 8:8, _:9, StreamID:31, _:1, Increment:31, Rest/bits >>) -> - {ok, {window_update, StreamID, Increment}, Rest}; -parse(<< Len:24, 8:8, _/bits >>) when Len =/= 4-> - {connection_error, frame_size_error, 'WINDOW_UPDATE frames MUST be 4 bytes wide. (RFC7540 6.9)'}; -%% -%% CONTINUATION frames. -%% -parse(<< _:24, 9:8, _:9, 0:31, _/bits >>) -> - {connection_error, protocol_error, 'CONTINUATION frames MUST be associated with a stream. (RFC7540 6.10)'}; -parse(<< Len:24, 9:8, _:5, FlagEndHeaders:1, _:3, StreamID:31, HeaderBlockFragment:Len/binary, Rest/bits >>) -> - {ok, {continuation, StreamID, parse_head_fin(FlagEndHeaders), HeaderBlockFragment}, Rest}; -%% -%% Unknown frames are ignored. -%% -parse(<< Len:24, Type:8, _:40, _:Len/binary, Rest/bits >>) when Type > 9 -> - {ignore, Rest}; -%% -%% Incomplete frames. -%% -parse(_) -> - more. - -%% Note: Original TEST block removed. See hackney_http2_tests.erl for tests. - -parse_fin(0) -> nofin; -parse_fin(1) -> fin. - -parse_head_fin(0) -> head_nofin; -parse_head_fin(1) -> head_fin. - -parse_exclusive(0) -> shared; -parse_exclusive(1) -> exclusive. - -parse_error_code( 0) -> no_error; -parse_error_code( 1) -> protocol_error; -parse_error_code( 2) -> internal_error; -parse_error_code( 3) -> flow_control_error; -parse_error_code( 4) -> settings_timeout; -parse_error_code( 5) -> stream_closed; -parse_error_code( 6) -> frame_size_error; -parse_error_code( 7) -> refused_stream; -parse_error_code( 8) -> cancel; -parse_error_code( 9) -> compression_error; -parse_error_code(10) -> connect_error; -parse_error_code(11) -> enhance_your_calm; -parse_error_code(12) -> inadequate_security; -parse_error_code(13) -> http_1_1_required; -parse_error_code(_) -> unknown_error. - -parse_settings_payload(SettingsPayload) -> - {ok, {settings, Settings}, <<>>} - = parse_settings_payload(SettingsPayload, byte_size(SettingsPayload), #{}), - Settings. - -parse_settings_payload(Rest, 0, Settings) -> - {ok, {settings, Settings}, Rest}; -%% SETTINGS_HEADER_TABLE_SIZE. -parse_settings_payload(<< 1:16, Value:32, Rest/bits >>, Len, Settings) -> - parse_settings_payload(Rest, Len - 6, Settings#{header_table_size => Value}); -%% SETTINGS_ENABLE_PUSH. -parse_settings_payload(<< 2:16, 0:32, Rest/bits >>, Len, Settings) -> - parse_settings_payload(Rest, Len - 6, Settings#{enable_push => false}); -parse_settings_payload(<< 2:16, 1:32, Rest/bits >>, Len, Settings) -> - parse_settings_payload(Rest, Len - 6, Settings#{enable_push => true}); -parse_settings_payload(<< 2:16, _:32, _/bits >>, _, _) -> - {connection_error, protocol_error, 'The SETTINGS_ENABLE_PUSH value MUST be 0 or 1. (RFC7540 6.5.2)'}; -%% SETTINGS_MAX_CONCURRENT_STREAMS. -parse_settings_payload(<< 3:16, Value:32, Rest/bits >>, Len, Settings) -> - parse_settings_payload(Rest, Len - 6, Settings#{max_concurrent_streams => Value}); -%% SETTINGS_INITIAL_WINDOW_SIZE. -parse_settings_payload(<< 4:16, Value:32, _/bits >>, _, _) when Value > 16#7fffffff -> - {connection_error, flow_control_error, 'The maximum SETTINGS_INITIAL_WINDOW_SIZE value is 0x7fffffff. (RFC7540 6.5.2)'}; -parse_settings_payload(<< 4:16, Value:32, Rest/bits >>, Len, Settings) -> - parse_settings_payload(Rest, Len - 6, Settings#{initial_window_size => Value}); -%% SETTINGS_MAX_FRAME_SIZE. -parse_settings_payload(<< 5:16, Value:32, _/bits >>, _, _) when Value =< 16#3fff -> - {connection_error, protocol_error, 'The SETTINGS_MAX_FRAME_SIZE value must be > 0x3fff. (RFC7540 6.5.2)'}; -parse_settings_payload(<< 5:16, Value:32, Rest/bits >>, Len, Settings) when Value =< 16#ffffff -> - parse_settings_payload(Rest, Len - 6, Settings#{max_frame_size => Value}); -parse_settings_payload(<< 5:16, _:32, _/bits >>, _, _) -> - {connection_error, protocol_error, 'The SETTINGS_MAX_FRAME_SIZE value must be =< 0xffffff. (RFC7540 6.5.2)'}; -%% SETTINGS_MAX_HEADER_LIST_SIZE. -parse_settings_payload(<< 6:16, Value:32, Rest/bits >>, Len, Settings) -> - parse_settings_payload(Rest, Len - 6, Settings#{max_header_list_size => Value}); -%% SETTINGS_ENABLE_CONNECT_PROTOCOL. -parse_settings_payload(<< 8:16, 0:32, Rest/bits >>, Len, Settings) -> - parse_settings_payload(Rest, Len - 6, Settings#{enable_connect_protocol => false}); -parse_settings_payload(<< 8:16, 1:32, Rest/bits >>, Len, Settings) -> - parse_settings_payload(Rest, Len - 6, Settings#{enable_connect_protocol => true}); -parse_settings_payload(<< 8:16, _:32, _/bits >>, _, _) -> - {connection_error, protocol_error, 'The SETTINGS_ENABLE_CONNECT_PROTOCOL value MUST be 0 or 1. (draft-h2-websockets-01 3)'}; -%% Ignore unknown settings. -parse_settings_payload(<< _:48, Rest/bits >>, Len, Settings) -> - parse_settings_payload(Rest, Len - 6, Settings). - -%% Building. - -data(StreamID, IsFin, Data) -> - [data_header(StreamID, IsFin, iolist_size(Data)), Data]. - -data_header(StreamID, IsFin, Len) -> - FlagEndStream = flag_fin(IsFin), - << Len:24, 0:15, FlagEndStream:1, 0:1, StreamID:31 >>. - -%% @todo Check size of HeaderBlock and use CONTINUATION frames if needed. -headers(StreamID, IsFin, HeaderBlock) -> - Len = iolist_size(HeaderBlock), - FlagEndStream = flag_fin(IsFin), - FlagEndHeaders = 1, - [<< Len:24, 1:8, 0:5, FlagEndHeaders:1, 0:1, FlagEndStream:1, 0:1, StreamID:31 >>, HeaderBlock]. - -priority(StreamID, E, DepStreamID, Weight) -> - FlagExclusive = exclusive(E), - << 5:24, 2:8, 0:9, StreamID:31, FlagExclusive:1, DepStreamID:31, Weight:8 >>. - -rst_stream(StreamID, Reason) -> - ErrorCode = error_code(Reason), - << 4:24, 3:8, 0:9, StreamID:31, ErrorCode:32 >>. - -settings(Settings) -> - Payload = settings_payload(Settings), - Len = iolist_size(Payload), - [<< Len:24, 4:8, 0:40 >>, Payload]. - -settings_payload(Settings) -> - [case Key of - header_table_size -> <<1:16, Value:32>>; - enable_push when Value -> <<2:16, 1:32>>; - enable_push -> <<2:16, 0:32>>; - max_concurrent_streams when Value =:= infinity -> <<>>; - max_concurrent_streams -> <<3:16, Value:32>>; - initial_window_size -> <<4:16, Value:32>>; - max_frame_size -> <<5:16, Value:32>>; - max_header_list_size when Value =:= infinity -> <<>>; - max_header_list_size -> <<6:16, Value:32>>; - enable_connect_protocol when Value -> <<8:16, 1:32>>; - enable_connect_protocol -> <<8:16, 0:32>> - end || {Key, Value} <- maps:to_list(Settings)]. - -settings_ack() -> - << 0:24, 4:8, 1:8, 0:32 >>. - -%% @todo Check size of HeaderBlock and use CONTINUATION frames if needed. -push_promise(StreamID, PromisedStreamID, HeaderBlock) -> - Len = iolist_size(HeaderBlock) + 4, - FlagEndHeaders = 1, - [<< Len:24, 5:8, 0:5, FlagEndHeaders:1, 0:3, StreamID:31, 0:1, PromisedStreamID:31 >>, HeaderBlock]. - -ping(Opaque) -> - << 8:24, 6:8, 0:40, Opaque:64 >>. - -ping_ack(Opaque) -> - << 8:24, 6:8, 0:7, 1:1, 0:32, Opaque:64 >>. - -goaway(LastStreamID, Reason, DebugData) -> - ErrorCode = error_code(Reason), - Len = iolist_size(DebugData) + 8, - [<< Len:24, 7:8, 0:41, LastStreamID:31, ErrorCode:32 >>, DebugData]. - -window_update(Increment) -> - window_update(0, Increment). - -window_update(StreamID, Increment) when Increment =< 16#7fffffff -> - << 4:24, 8:8, 0:8, StreamID:32, 0:1, Increment:31 >>. - -flag_fin(nofin) -> 0; -flag_fin(fin) -> 1. - -exclusive(shared) -> 0; -exclusive(exclusive) -> 1. - -error_code(no_error) -> 0; -error_code(protocol_error) -> 1; -error_code(internal_error) -> 2; -error_code(flow_control_error) -> 3; -error_code(settings_timeout) -> 4; -error_code(stream_closed) -> 5; -error_code(frame_size_error) -> 6; -error_code(refused_stream) -> 7; -error_code(cancel) -> 8; -error_code(compression_error) -> 9; -error_code(connect_error) -> 10; -error_code(enhance_your_calm) -> 11; -error_code(inadequate_security) -> 12; -error_code(http_1_1_required) -> 13. diff --git a/src/hackney_http2_machine.erl b/src/hackney_http2_machine.erl deleted file mode 100644 index b5cc2867..00000000 --- a/src/hackney_http2_machine.erl +++ /dev/null @@ -1,1819 +0,0 @@ -%%% -*- erlang -*- -%%% -%%% Based on cow_http2_machine from cowlib (https://github.com/ninenines/cowlib) -%%% Modified for hackney with performance optimizations. -%%% -%% Copyright (c) 2018-2024, Loïc Hoguin -%% Copyright (c) 2026, Benoit Chesneau -%% -%% Permission to use, copy, modify, and/or distribute this software for any -%% purpose with or without fee is hereby granted, provided that the above -%% copyright notice and this permission notice appear in all copies. -%% -%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - --module(hackney_http2_machine). - --export([init/2]). --export([init_stream/2]). --export([init_upgrade_stream/2]). --export([frame/2]). --export([ignored_frame/1]). --export([timeout/3]). --export([prepare_headers/5]). --export([prepare_push_promise/4]). --export([prepare_trailers/3]). --export([send_or_queue_data/4]). --export([ensure_window/2]). --export([ensure_window/3]). --export([update_window/2]). --export([update_window/3]). --export([reset_stream/2]). --export([get_connection_local_buffer_size/1]). --export([get_local_setting/2]). --export([get_remote_settings/1]). --export([get_last_streamid/1]). --export([set_last_streamid/1]). --export([get_stream_local_buffer_size/2]). --export([get_stream_local_state/2]). --export([get_stream_remote_state/2]). --export([is_lingering_stream/2]). - --type opts() :: #{ - connection_window_margin_size => 0..16#7fffffff, - connection_window_update_threshold => 0..16#7fffffff, - enable_connect_protocol => boolean(), - initial_connection_window_size => 65535..16#7fffffff, - initial_stream_window_size => 0..16#7fffffff, - max_connection_window_size => 0..16#7fffffff, - max_concurrent_streams => non_neg_integer() | infinity, - max_decode_table_size => non_neg_integer(), - max_encode_table_size => non_neg_integer(), - max_fragmented_header_block_size => 16384..16#7fffffff, - max_frame_size_received => 16384..16777215, - max_frame_size_sent => 16384..16777215 | infinity, - max_stream_window_size => 0..16#7fffffff, - message_tag => any(), - preface_timeout => timeout(), - settings_timeout => timeout(), - stream_window_data_threshold => 0..16#7fffffff, - stream_window_margin_size => 0..16#7fffffff, - stream_window_update_threshold => 0..16#7fffffff -}. --export_type([opts/0]). - -%% Local types to replace cow_http types --type status() :: 100..999. --type headers() :: [{binary(), iodata()}]. - -%% The order of the fields is significant. --record(sendfile, { - offset :: non_neg_integer(), - bytes :: pos_integer(), - path :: file:name_all() -}). - --record(stream, { - id = undefined :: hackney_http2:streamid(), - - %% Request method. - method = undefined :: binary(), - - %% Whether we finished sending data. - local = idle :: idle | hackney_http2:fin(), - - %% Local flow control window (how much we can send). - local_window :: integer(), - - %% Buffered data waiting for the flow control window to increase. - local_buffer = queue:new() :: - queue:queue({hackney_http2:fin(), non_neg_integer(), {data, iodata()} | #sendfile{}}), - local_buffer_size = 0 :: non_neg_integer(), - local_trailers = undefined :: undefined | headers(), - - %% Whether we finished receiving data. - remote = idle :: idle | hackney_http2:fin(), - - %% Remote flow control window (how much we accept to receive). - remote_window :: integer(), - - %% Size expected and read from the request body. - remote_expected_size = undefined :: undefined | non_neg_integer(), - remote_read_size = 0 :: non_neg_integer(), - - %% Unparsed te header. Used to know if we can send trailers. - %% Note that we can always send trailers to the server. - te :: undefined | binary() -}). - --type stream() :: #stream{}. - --type continued_frame() :: - {headers, hackney_http2:streamid(), hackney_http2:fin(), hackney_http2:head_fin(), iodata()} | - {push_promise, hackney_http2:streamid(), hackney_http2:head_fin(), hackney_http2:streamid(), iodata()}. - --record(http2_machine, { - %% Whether the HTTP/2 endpoint is a client or a server. - mode :: client | server, - - %% HTTP/2 SETTINGS customization. - opts = #{} :: opts(), - - %% Connection-wide frame processing state. - state = settings :: settings | normal - | {continuation, request | response | trailers | push_promise, continued_frame()}, - - %% Timer for the connection preface. - preface_timer = undefined :: undefined | reference(), - - %% Timer for the ack for a SETTINGS frame we sent. - settings_timer = undefined :: undefined | reference(), - - %% Settings are separate for each endpoint. In addition, settings - %% must be acknowledged before they can be expected to be applied. - local_settings = #{ -% header_table_size => 4096, -% enable_push => true, -% max_concurrent_streams => infinity, - initial_window_size => 65535 -% max_frame_size => 16384 -% max_header_list_size => infinity - } :: map(), - next_settings = undefined :: undefined | map(), - remote_settings = #{ - initial_window_size => 65535 - } :: map(), - - %% Connection-wide flow control window. - local_window = 65535 :: integer(), %% How much we can send. - remote_window = 65535 :: integer(), %% How much we accept to receive. - - %% Stream identifiers. - local_streamid :: pos_integer(), %% The next streamid to be used. - remote_streamid = 0 :: non_neg_integer(), %% The last streamid received. - last_remote_streamid = 16#7fffffff :: non_neg_integer(), %% Used in GOAWAY. - - %% Currently active HTTP/2 streams. Streams may be initiated either - %% by the client or by the server through PUSH_PROMISE frames. - streams = #{} :: #{hackney_http2:streamid() => stream()}, - - %% OPTIMIZATION: Cache for recently accessed stream to avoid repeated map lookups. - %% DATA frames, HEADERS, WINDOW_UPDATE all lookup same stream repeatedly. - %% Set to undefined when cache is invalid (stream closed, stored, etc.) - active_stream = undefined :: undefined | {hackney_http2:streamid(), stream()}, - - %% HTTP/2 streams that have recently been reset locally. - %% We are expected to keep receiving additional frames after - %% sending an RST_STREAM. - %% OPTIMIZATION: Use gb_sets for O(log N) lookup instead of O(N) list. - local_lingering_streams = gb_sets:empty() :: gb_sets:set(hackney_http2:streamid()), - local_lingering_count = 0 :: non_neg_integer(), - - %% HTTP/2 streams that have recently been reset remotely. - %% We keep a few of these around in order to reject subsequent - %% frames on these streams. - %% OPTIMIZATION: Use gb_sets for O(log N) lookup instead of O(N) list. - remote_lingering_streams = gb_sets:empty() :: gb_sets:set(hackney_http2:streamid()), - remote_lingering_count = 0 :: non_neg_integer(), - - %% HPACK decoding and encoding state. - decode_state = hackney_hpack:init() :: hackney_hpack:state(), - encode_state = hackney_hpack:init() :: hackney_hpack:state() -}). - --type http2_machine() :: #http2_machine{}. --export_type([http2_machine/0]). - --type pseudo_headers() :: #{} %% Trailers - | #{ %% Responses. - status := status() - } | #{ %% Normal CONNECT requests. - method := binary(), - authority := binary() - } | #{ %% Other requests and extended CONNECT requests. - method := binary(), - scheme := binary(), - authority := binary(), - path := binary(), - protocol => binary() - }. - -%% Returns true when the given StreamID is for a local-initiated stream. --define(IS_SERVER_LOCAL(StreamID), ((StreamID rem 2) =:= 0)). --define(IS_CLIENT_LOCAL(StreamID), ((StreamID rem 2) =:= 1)). --define(IS_LOCAL(Mode, StreamID), ( - ((Mode =:= server) andalso ?IS_SERVER_LOCAL(StreamID)) - orelse - ((Mode =:= client) andalso ?IS_CLIENT_LOCAL(StreamID)) -)). - -%% Maximum number of lingering streams to keep. --define(MAX_LOCAL_LINGERING, 100). --define(MAX_REMOTE_LINGERING, 10). - --spec init(client | server, opts()) -> {ok, iodata(), http2_machine()}. -init(client, Opts) -> - NextSettings = settings_init(Opts), - client_preface(#http2_machine{ - mode=client, - opts=Opts, - preface_timer=start_timer(preface_timeout, Opts), - settings_timer=start_timer(settings_timeout, Opts), - next_settings=NextSettings, - local_streamid=1 - }); -init(server, Opts) -> - NextSettings = settings_init(Opts), - common_preface(#http2_machine{ - mode=server, - opts=Opts, - preface_timer=start_timer(preface_timeout, Opts), - settings_timer=start_timer(settings_timeout, Opts), - next_settings=NextSettings, - local_streamid=2 - }). - -%% @todo In Cowlib 3.0 we should always include MessageTag in the message. -%% It can be set to 'undefined' if the option is missing. -start_timer(Name, Opts=#{message_tag := MessageTag}) -> - case maps:get(Name, Opts, 5000) of - infinity -> undefined; - Timeout -> erlang:start_timer(Timeout, self(), {?MODULE, MessageTag, Name}) - end; -start_timer(Name, Opts) -> - case maps:get(Name, Opts, 5000) of - infinity -> undefined; - Timeout -> erlang:start_timer(Timeout, self(), {?MODULE, Name}) - end. - -client_preface(State0) -> - {ok, CommonPreface, State} = common_preface(State0), - {ok, [ - <<"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n">>, - CommonPreface - ], State}. - -%% We send next_settings and use defaults until we get an ack. -%% -%% We also send a WINDOW_UPDATE frame for the connection when -%% the user specified an initial_connection_window_size. -common_preface(State=#http2_machine{opts=Opts, next_settings=NextSettings}) -> - case maps:get(initial_connection_window_size, Opts, 65535) of - 65535 -> - {ok, hackney_http2:settings(NextSettings), State}; - Size -> - {ok, [ - hackney_http2:settings(NextSettings), - hackney_http2:window_update(Size - 65535) - ], update_window(Size - 65535, State)} - end. - -settings_init(Opts) -> - S0 = setting_from_opt(#{}, Opts, max_decode_table_size, - header_table_size, 4096), - S1 = setting_from_opt(S0, Opts, max_concurrent_streams, - max_concurrent_streams, infinity), - S2 = setting_from_opt(S1, Opts, initial_stream_window_size, - initial_window_size, 65535), - S3 = setting_from_opt(S2, Opts, max_frame_size_received, - max_frame_size, 16384), - %% @todo max_header_list_size - setting_from_opt(S3, Opts, enable_connect_protocol, - enable_connect_protocol, false). - -setting_from_opt(Settings, Opts, OptName, SettingName, Default) -> - case maps:get(OptName, Opts, Default) of - Default -> Settings; - Value -> Settings#{SettingName => Value} - end. - --spec init_stream(binary(), State) - -> {ok, hackney_http2:streamid(), State} when State::http2_machine(). -init_stream(Method, State=#http2_machine{mode=client, local_streamid=LocalStreamID, - local_settings=#{initial_window_size := RemoteWindow}, - remote_settings=#{initial_window_size := LocalWindow}}) -> - Stream = #stream{id=LocalStreamID, method=Method, - local_window=LocalWindow, remote_window=RemoteWindow}, - {ok, LocalStreamID, stream_store(Stream, State#http2_machine{ - local_streamid=LocalStreamID + 2})}. - --spec init_upgrade_stream(binary(), State) - -> {ok, hackney_http2:streamid(), State} when State::http2_machine(). -init_upgrade_stream(Method, State=#http2_machine{mode=server, remote_streamid=0, - local_settings=#{initial_window_size := RemoteWindow}, - remote_settings=#{initial_window_size := LocalWindow}}) -> - Stream = #stream{id=1, method=Method, - remote=fin, remote_expected_size=0, - local_window=LocalWindow, remote_window=RemoteWindow, te=undefined}, - {ok, 1, stream_store(Stream, State#http2_machine{remote_streamid=1})}. - --spec frame(hackney_http2:frame(), State) - -> {ok, State} - | {ok, {data, hackney_http2:streamid(), hackney_http2:fin(), binary()}, State} - | {ok, {headers, hackney_http2:streamid(), hackney_http2:fin(), - headers(), pseudo_headers(), non_neg_integer() | undefined}, State} - | {ok, {trailers, hackney_http2:streamid(), headers()}, State} - | {ok, {rst_stream, hackney_http2:streamid(), hackney_http2:error()}, State} - | {ok, {push_promise, hackney_http2:streamid(), hackney_http2:streamid(), - headers(), pseudo_headers()}, State} - | {ok, {goaway, hackney_http2:streamid(), hackney_http2:error(), binary()}, State} - | {send, [{hackney_http2:streamid(), hackney_http2:fin(), - [{data, iodata()} | #sendfile{} | {trailers, headers()}]}], State} - | {error, {stream_error, hackney_http2:streamid(), hackney_http2:error(), atom()}, State} - | {error, {connection_error, hackney_http2:error(), atom()}, State} - when State::http2_machine(). -frame(Frame, State=#http2_machine{state=settings, preface_timer=TRef}) -> - ok = case TRef of - undefined -> ok; - _ -> erlang:cancel_timer(TRef, [{async, true}, {info, false}]) - end, - settings_frame(Frame, State#http2_machine{state=normal, preface_timer=undefined}); -frame(Frame, State=#http2_machine{state={continuation, _, _}}) -> - maybe_discard_result(continuation_frame(Frame, State)); -frame(settings_ack, State=#http2_machine{state=normal}) -> - settings_ack_frame(State); -frame(Frame, State=#http2_machine{state=normal}) -> - Result = case element(1, Frame) of - data -> data_frame(Frame, State); - headers -> headers_frame(Frame, State); - priority -> priority_frame(Frame, State); - rst_stream -> rst_stream_frame(Frame, State); - settings -> settings_frame(Frame, State); - push_promise -> push_promise_frame(Frame, State); - ping -> ping_frame(Frame, State); - ping_ack -> ping_ack_frame(Frame, State); - goaway -> goaway_frame(Frame, State); - window_update -> window_update_frame(Frame, State); - continuation -> unexpected_continuation_frame(Frame, State); - _ -> ignored_frame(State) - end, - maybe_discard_result(Result). - -%% RFC7540 6.9. After sending a GOAWAY frame, the sender can discard frames for -%% streams initiated by the receiver with identifiers higher than the identified -%% last stream. However, any frames that alter connection state cannot be -%% completely ignored. For instance, HEADERS, PUSH_PROMISE, and CONTINUATION -%% frames MUST be minimally processed to ensure the state maintained for header -%% compression is consistent. -maybe_discard_result(FrameResult={ok, Result, State=#http2_machine{mode=Mode, - last_remote_streamid=MaxID}}) - when element(1, Result) =/= goaway -> - case element(2, Result) of - StreamID when StreamID > MaxID, not ?IS_LOCAL(Mode, StreamID) -> - {ok, State}; - _StreamID -> - FrameResult - end; -maybe_discard_result(FrameResult) -> - FrameResult. - -%% DATA frame. - -data_frame({data, StreamID, _, _}, State=#http2_machine{mode=Mode, - local_streamid=LocalStreamID, remote_streamid=RemoteStreamID}) - when (?IS_LOCAL(Mode, StreamID) andalso (StreamID >= LocalStreamID)) - orelse ((not ?IS_LOCAL(Mode, StreamID)) andalso (StreamID > RemoteStreamID)) -> - {error, {connection_error, protocol_error, - 'DATA frame received on a stream in idle state. (RFC7540 5.1)'}, - State}; -data_frame({data, _, _, Data}, State=#http2_machine{remote_window=ConnWindow}) - when byte_size(Data) > ConnWindow -> - {error, {connection_error, flow_control_error, - 'DATA frame overflowed the connection flow control window. (RFC7540 6.9, RFC7540 6.9.1)'}, - State}; -data_frame(Frame={data, StreamID, _, Data}, State0=#http2_machine{ - remote_window=ConnWindow, local_lingering_streams=Lingering}) -> - DataLen = byte_size(Data), - State = State0#http2_machine{remote_window=ConnWindow - DataLen}, - case stream_get(StreamID, State) of - #stream{remote_window=StreamWindow} when StreamWindow < DataLen -> - stream_reset(StreamID, State, flow_control_error, - 'DATA frame overflowed the stream flow control window. (RFC7540 6.9, RFC7540 6.9.1)'); - Stream = #stream{remote=nofin} -> - data_frame(Frame, State, Stream, DataLen); - #stream{remote=idle} -> - stream_reset(StreamID, State, protocol_error, - 'DATA frame received before a HEADERS frame. (RFC7540 8.1, RFC7540 8.1.2.6)'); - #stream{remote=fin} -> - stream_reset(StreamID, State, stream_closed, - 'DATA frame received for a half-closed (remote) stream. (RFC7540 5.1)'); - undefined -> - %% After we send an RST_STREAM frame and terminate a stream, - %% the remote endpoint might still be sending us some more - %% frames until it can process this RST_STREAM. - case gb_sets:is_member(StreamID, Lingering) of - true -> - {ok, State}; - false -> - {error, {connection_error, stream_closed, - 'DATA frame received for a closed stream. (RFC7540 5.1)'}, - State} - end - end. - -data_frame(Frame={data, _, IsFin, _}, State0, Stream0=#stream{id=StreamID, - remote_window=StreamWindow, remote_read_size=StreamRead}, DataLen) -> - Stream = Stream0#stream{remote=IsFin, - remote_window=StreamWindow - DataLen, - remote_read_size=StreamRead + DataLen}, - State = stream_store(Stream, State0), - case is_body_size_valid(Stream) of - true -> - {ok, Frame, State}; - false -> - stream_reset(StreamID, State, protocol_error, - 'The total size of DATA frames is different than the content-length. (RFC7540 8.1.2.6)') - end. - -%% It's always valid when no content-length header was specified. -is_body_size_valid(#stream{remote_expected_size=undefined}) -> - true; -%% We didn't finish reading the body but the size is already larger than expected. -is_body_size_valid(#stream{remote=nofin, remote_expected_size=Expected, - remote_read_size=Read}) when Read > Expected -> - false; -is_body_size_valid(#stream{remote=nofin}) -> - true; -is_body_size_valid(#stream{remote=fin, remote_expected_size=Expected, - remote_read_size=Expected}) -> - true; -%% We finished reading the body and the size read is not the one expected. -is_body_size_valid(_) -> - false. - -%% HEADERS frame. -%% -%% We always close the connection when we detect errors before -%% decoding the headers to not waste resources on non-compliant -%% endpoints, making us stricter than the RFC requires. - -%% Convenience record to manipulate the tuple. -%% The order of the fields matter. --record(headers, { - id :: hackney_http2:streamid(), - fin :: hackney_http2:fin(), - head :: hackney_http2:head_fin(), - data :: iodata() -}). - -headers_frame(Frame=#headers{}, State=#http2_machine{mode=Mode}) -> - case Mode of - server -> server_headers_frame(Frame, State); - client -> client_headers_frame(Frame, State) - end; -%% @todo Handle the PRIORITY data, but only if this returns an ok tuple. -%% @todo Do not lose the PRIORITY information if CONTINUATION frames follow. -headers_frame({headers, StreamID, IsFin, IsHeadFin, - _IsExclusive, _DepStreamID, _Weight, HeaderData}, - State=#http2_machine{mode=Mode}) -> - HeadersFrame = #headers{id=StreamID, fin=IsFin, head=IsHeadFin, data=HeaderData}, - case Mode of - server -> server_headers_frame(HeadersFrame, State); - client -> client_headers_frame(HeadersFrame, State) - end. - -%% Reject HEADERS frames with even-numbered streamid. -server_headers_frame(#headers{id=StreamID}, State) - when ?IS_SERVER_LOCAL(StreamID) -> - {error, {connection_error, protocol_error, - 'HEADERS frame received with even-numbered streamid. (RFC7540 5.1.1)'}, - State}; -%% HEADERS frame on an idle stream: new request. -server_headers_frame(Frame=#headers{id=StreamID, head=IsHeadFin}, - State=#http2_machine{mode=server, remote_streamid=RemoteStreamID}) - when StreamID > RemoteStreamID -> - case IsHeadFin of - head_fin -> - headers_decode(Frame, State, request, undefined); - head_nofin -> - {ok, State#http2_machine{state={continuation, request, Frame}}} - end; -%% Either a HEADERS frame received on (half-)closed stream, -%% or a HEADERS frame containing the trailers. -server_headers_frame(Frame=#headers{id=StreamID, fin=IsFin, head=IsHeadFin}, State) -> - case stream_get(StreamID, State) of - %% Trailers. - Stream = #stream{remote=nofin} when IsFin =:= fin -> - case IsHeadFin of - head_fin -> - headers_decode(Frame, State, trailers, Stream); - head_nofin -> - {ok, State#http2_machine{state={continuation, trailers, Frame}}} - end; - #stream{remote=nofin} -> - {error, {connection_error, protocol_error, - 'Trailing HEADERS frame received without the END_STREAM flag set. (RFC7540 8.1, RFC7540 8.1.2.6)'}, - State}; - _ -> - {error, {connection_error, stream_closed, - 'HEADERS frame received on a stream in closed or half-closed state. (RFC7540 5.1)'}, - State} - end. - -%% Either a HEADERS frame received on an (half-)closed stream, -%% or a HEADERS frame containing the response or the trailers. -client_headers_frame(Frame=#headers{id=StreamID, fin=IsFin, head=IsHeadFin}, - State=#http2_machine{local_streamid=LocalStreamID, remote_streamid=RemoteStreamID}) - when (?IS_CLIENT_LOCAL(StreamID) andalso (StreamID < LocalStreamID)) - orelse ((not ?IS_CLIENT_LOCAL(StreamID)) andalso (StreamID =< RemoteStreamID)) -> - case stream_get(StreamID, State) of - Stream = #stream{remote=idle} -> - case IsHeadFin of - head_fin -> - headers_decode(Frame, State, response, Stream); - head_nofin -> - {ok, State#http2_machine{state={continuation, response, Frame}}} - end; - Stream = #stream{remote=nofin} when IsFin =:= fin -> - case IsHeadFin of - head_fin -> - headers_decode(Frame, State, trailers, Stream); - head_nofin -> - {ok, State#http2_machine{state={continuation, trailers, Frame}}} - end; - #stream{remote=nofin} -> - {error, {connection_error, protocol_error, - 'Trailing HEADERS frame received without the END_STREAM flag set. (RFC7540 8.1, RFC7540 8.1.2.6)'}, - State}; - _ -> - {error, {connection_error, stream_closed, - 'HEADERS frame received on a stream in closed or half-closed state. (RFC7540 5.1)'}, - State} - end; -%% Reject HEADERS frames received on idle streams. -client_headers_frame(_, State) -> - {error, {connection_error, protocol_error, - 'HEADERS frame received on an idle stream. (RFC7540 5.1.1)'}, - State}. - -headers_decode(Frame=#headers{head=head_fin, data=HeaderData}, - State=#http2_machine{decode_state=DecodeState0}, Type, Stream) -> - try hackney_hpack:decode(iolist_to_binary(HeaderData), DecodeState0) of - {Headers, DecodeState} when Type =:= request -> - headers_enforce_concurrency_limit(Frame, - State#http2_machine{decode_state=DecodeState}, Type, Stream, Headers); - {Headers, DecodeState} -> - headers_pseudo_headers(Frame, - State#http2_machine{decode_state=DecodeState}, Type, Stream, Headers) - catch _:_ -> - {error, {connection_error, compression_error, - 'Error while trying to decode HPACK-encoded header block. (RFC7540 4.3)'}, - State} - end. - -headers_enforce_concurrency_limit(Frame=#headers{id=StreamID}, - State=#http2_machine{local_settings=LocalSettings, streams=Streams}, - Type, Stream, Headers) -> - MaxConcurrentStreams = maps:get(max_concurrent_streams, LocalSettings, infinity), - %% Using < is correct because this new stream is not included - %% in the Streams variable yet and so we'll end up with +1 stream. - case map_size(Streams) < MaxConcurrentStreams of - true -> - headers_pseudo_headers(Frame, State, Type, Stream, Headers); - false -> - {error, {stream_error, StreamID, refused_stream, - 'Maximum number of concurrent streams has been reached. (RFC7540 5.1.2)'}, - State} - end. - -headers_pseudo_headers(Frame, State=#http2_machine{local_settings=LocalSettings}, - Type, Stream, Headers0) when Type =:= request; Type =:= push_promise -> - IsExtendedConnectEnabled = maps:get(enable_connect_protocol, LocalSettings, false), - case request_pseudo_headers(Headers0, #{}) of - %% Extended CONNECT method (RFC8441). - {ok, PseudoHeaders=#{method := <<"CONNECT">>, scheme := _, - authority := _, path := _, protocol := _}, Headers} - when IsExtendedConnectEnabled -> - headers_regular_headers(Frame, State, Type, Stream, PseudoHeaders, Headers); - {ok, #{method := <<"CONNECT">>, scheme := _, - authority := _, path := _}, _} - when IsExtendedConnectEnabled -> - headers_malformed(Frame, State, - 'The :protocol pseudo-header MUST be sent with an extended CONNECT. (RFC8441 4)'); - {ok, #{protocol := _}, _} -> - headers_malformed(Frame, State, - 'The :protocol pseudo-header is only defined for the extended CONNECT. (RFC8441 4)'); - %% Normal CONNECT (no scheme/path). - {ok, PseudoHeaders=#{method := <<"CONNECT">>, authority := _}, Headers} - when map_size(PseudoHeaders) =:= 2 -> - headers_regular_headers(Frame, State, Type, Stream, PseudoHeaders, Headers); - {ok, #{method := <<"CONNECT">>}, _} -> - headers_malformed(Frame, State, - 'CONNECT requests only use the :method and :authority pseudo-headers. (RFC7540 8.3)'); - %% Other requests. - {ok, PseudoHeaders=#{method := _, scheme := _, path := _}, Headers} -> - headers_regular_headers(Frame, State, Type, Stream, PseudoHeaders, Headers); - {ok, _, _} -> - headers_malformed(Frame, State, - 'A required pseudo-header was not found. (RFC7540 8.1.2.3)'); - {error, HumanReadable} -> - headers_malformed(Frame, State, HumanReadable) - end; -headers_pseudo_headers(Frame=#headers{id=StreamID}, - State, Type=response, Stream, Headers0) -> - case response_pseudo_headers(Headers0, #{}) of - {ok, PseudoHeaders=#{status := _}, Headers} -> - headers_regular_headers(Frame, State, Type, Stream, PseudoHeaders, Headers); - {ok, _, _} -> - stream_reset(StreamID, State, protocol_error, - 'A required pseudo-header was not found. (RFC7540 8.1.2.4)'); - {error, HumanReadable} -> - stream_reset(StreamID, State, protocol_error, HumanReadable) - end; -headers_pseudo_headers(Frame=#headers{id=StreamID}, - State, Type=trailers, Stream, Headers) -> - case trailers_contain_pseudo_headers(Headers) of - false -> - headers_regular_headers(Frame, State, Type, Stream, #{}, Headers); - true -> - stream_reset(StreamID, State, protocol_error, - 'Trailer header blocks must not contain pseudo-headers. (RFC7540 8.1.2.1)') - end. - -headers_malformed(#headers{id=StreamID}, State, HumanReadable) -> - {error, {stream_error, StreamID, protocol_error, HumanReadable}, State}. - -request_pseudo_headers([{<<":method">>, _}|_], #{method := _}) -> - {error, 'Multiple :method pseudo-headers were found. (RFC7540 8.1.2.3)'}; -request_pseudo_headers([{<<":method">>, Method}|Tail], PseudoHeaders) -> - request_pseudo_headers(Tail, PseudoHeaders#{method => Method}); -request_pseudo_headers([{<<":scheme">>, _}|_], #{scheme := _}) -> - {error, 'Multiple :scheme pseudo-headers were found. (RFC7540 8.1.2.3)'}; -request_pseudo_headers([{<<":scheme">>, Scheme}|Tail], PseudoHeaders) -> - request_pseudo_headers(Tail, PseudoHeaders#{scheme => Scheme}); -request_pseudo_headers([{<<":authority">>, _}|_], #{authority := _}) -> - {error, 'Multiple :authority pseudo-headers were found. (RFC7540 8.1.2.3)'}; -request_pseudo_headers([{<<":authority">>, Authority}|Tail], PseudoHeaders) -> - request_pseudo_headers(Tail, PseudoHeaders#{authority => Authority}); -request_pseudo_headers([{<<":path">>, _}|_], #{path := _}) -> - {error, 'Multiple :path pseudo-headers were found. (RFC7540 8.1.2.3)'}; -request_pseudo_headers([{<<":path">>, Path}|Tail], PseudoHeaders) -> - request_pseudo_headers(Tail, PseudoHeaders#{path => Path}); -request_pseudo_headers([{<<":protocol">>, _}|_], #{protocol := _}) -> - {error, 'Multiple :protocol pseudo-headers were found. (RFC7540 8.1.2.3)'}; -request_pseudo_headers([{<<":protocol">>, Protocol}|Tail], PseudoHeaders) -> - request_pseudo_headers(Tail, PseudoHeaders#{protocol => Protocol}); -request_pseudo_headers([{<<":", _/bits>>, _}|_], _) -> - {error, 'An unknown or invalid pseudo-header was found. (RFC7540 8.1.2.1)'}; -request_pseudo_headers(Headers, PseudoHeaders) -> - {ok, PseudoHeaders, Headers}. - -response_pseudo_headers([{<<":status">>, _}|_], #{status := _}) -> - {error, 'Multiple :status pseudo-headers were found. (RFC7540 8.1.2.3)'}; -response_pseudo_headers([{<<":status">>, Status}|Tail], PseudoHeaders) -> - try status_to_integer(Status) of - IntStatus -> - response_pseudo_headers(Tail, PseudoHeaders#{status => IntStatus}) - catch _:_ -> - {error, 'The :status pseudo-header value is invalid. (RFC7540 8.1.2.4)'} - end; -response_pseudo_headers([{<<":", _/bits>>, _}|_], _) -> - {error, 'An unknown or invalid pseudo-header was found. (RFC7540 8.1.2.1)'}; -response_pseudo_headers(Headers, PseudoHeaders) -> - {ok, PseudoHeaders, Headers}. - -trailers_contain_pseudo_headers([]) -> - false; -trailers_contain_pseudo_headers([{<<":", _/bits>>, _}|_]) -> - true; -trailers_contain_pseudo_headers([_|Tail]) -> - trailers_contain_pseudo_headers(Tail). - -%% Rejecting invalid regular headers might be a bit too strong for clients. -headers_regular_headers(Frame=#headers{id=StreamID}, - State, Type, Stream, PseudoHeaders, Headers) -> - case regular_headers(Headers, Type) of - ok when Type =:= request -> - request_expected_size(Frame, State, Type, Stream, PseudoHeaders, Headers); - ok when Type =:= push_promise -> - push_promise_frame(Frame, State, Stream, PseudoHeaders, Headers); - ok when Type =:= response -> - response_expected_size(Frame, State, Type, Stream, PseudoHeaders, Headers); - ok when Type =:= trailers -> - trailers_frame(Frame, State, Stream, Headers); - {error, HumanReadable} when Type =:= request -> - headers_malformed(Frame, State, HumanReadable); - {error, HumanReadable} -> - stream_reset(StreamID, State, protocol_error, HumanReadable) - end. - -regular_headers([{<<>>, _}|_], _) -> - {error, 'Empty header names are not valid regular headers. (CVE-2019-9516)'}; -regular_headers([{<<":", _/bits>>, _}|_], _) -> - {error, 'Pseudo-headers were found after regular headers. (RFC7540 8.1.2.1)'}; -regular_headers([{<<"connection">>, _}|_], _) -> - {error, 'The connection header is not allowed. (RFC7540 8.1.2.2)'}; -regular_headers([{<<"keep-alive">>, _}|_], _) -> - {error, 'The keep-alive header is not allowed. (RFC7540 8.1.2.2)'}; -regular_headers([{<<"proxy-authenticate">>, _}|_], _) -> - {error, 'The proxy-authenticate header is not allowed. (RFC7540 8.1.2.2)'}; -regular_headers([{<<"proxy-authorization">>, _}|_], _) -> - {error, 'The proxy-authorization header is not allowed. (RFC7540 8.1.2.2)'}; -regular_headers([{<<"transfer-encoding">>, _}|_], _) -> - {error, 'The transfer-encoding header is not allowed. (RFC7540 8.1.2.2)'}; -regular_headers([{<<"upgrade">>, _}|_], _) -> - {error, 'The upgrade header is not allowed. (RFC7540 8.1.2.2)'}; -regular_headers([{<<"te">>, Value}|_], request) when Value =/= <<"trailers">> -> - {error, 'The te header with a value other than "trailers" is not allowed. (RFC7540 8.1.2.2)'}; -regular_headers([{<<"te">>, _}|_], Type) when Type =/= request -> - {error, 'The te header is only allowed in request headers. (RFC7540 8.1.2.2)'}; -regular_headers([{Name, _}|Tail], Type) -> - Pattern = [ - <<$A>>, <<$B>>, <<$C>>, <<$D>>, <<$E>>, <<$F>>, <<$G>>, <<$H>>, <<$I>>, - <<$J>>, <<$K>>, <<$L>>, <<$M>>, <<$N>>, <<$O>>, <<$P>>, <<$Q>>, <<$R>>, - <<$S>>, <<$T>>, <<$U>>, <<$V>>, <<$W>>, <<$X>>, <<$Y>>, <<$Z>> - ], - case binary:match(Name, Pattern) of - nomatch -> regular_headers(Tail, Type); - _ -> {error, 'Header names must be lowercase. (RFC7540 8.1.2)'} - end; -regular_headers([], _) -> - ok. - -request_expected_size(Frame=#headers{fin=IsFin}, State, Type, Stream, PseudoHeaders, Headers) -> - case [CL || {<<"content-length">>, CL} <- Headers] of - [] when IsFin =:= fin -> - headers_frame(Frame, State, Type, Stream, PseudoHeaders, Headers, 0); - [] -> - headers_frame(Frame, State, Type, Stream, PseudoHeaders, Headers, undefined); - [<<"0">>] when IsFin =:= fin -> - headers_frame(Frame, State, Type, Stream, PseudoHeaders, Headers, 0); - [_] when IsFin =:= fin -> - headers_malformed(Frame, State, - 'HEADERS frame with the END_STREAM flag contains a non-zero content-length. (RFC7540 8.1.2.6)'); - [BinLen] -> - headers_parse_expected_size(Frame, State, Type, Stream, - PseudoHeaders, Headers, BinLen); - _ -> - headers_malformed(Frame, State, - 'Multiple content-length headers were received. (RFC7230 3.3.2)') - end. - -response_expected_size(Frame=#headers{id=StreamID, fin=IsFin}, State, Type, - Stream=#stream{method=Method}, PseudoHeaders=#{status := Status}, Headers) -> - case [CL || {<<"content-length">>, CL} <- Headers] of - [] when IsFin =:= fin -> - headers_frame(Frame, State, Type, Stream, PseudoHeaders, Headers, 0); - [] -> - headers_frame(Frame, State, Type, Stream, PseudoHeaders, Headers, undefined); - [_] when Status >= 100, Status =< 199 -> - stream_reset(StreamID, State, protocol_error, - 'Content-length header received in a 1xx response. (RFC7230 3.3.2)'); - [_] when Status =:= 204 -> - stream_reset(StreamID, State, protocol_error, - 'Content-length header received in a 204 response. (RFC7230 3.3.2)'); - [_] when Status >= 200, Status =< 299, Method =:= <<"CONNECT">> -> - stream_reset(StreamID, State, protocol_error, - 'Content-length header received in a 2xx response to a CONNECT request. (RFC7230 3.3.2).'); - %% Responses to HEAD requests, and 304 responses may contain - %% a content-length header that must be ignored. (RFC7230 3.3.2) - [_] when Method =:= <<"HEAD">> -> - headers_frame(Frame, State, Type, Stream, PseudoHeaders, Headers, 0); - [_] when Status =:= 304 -> - headers_frame(Frame, State, Type, Stream, PseudoHeaders, Headers, 0); - [<<"0">>] when IsFin =:= fin -> - headers_frame(Frame, State, Type, Stream, PseudoHeaders, Headers, 0); - [_] when IsFin =:= fin -> - stream_reset(StreamID, State, protocol_error, - 'HEADERS frame with the END_STREAM flag contains a non-zero content-length. (RFC7540 8.1.2.6)'); - [BinLen] -> - headers_parse_expected_size(Frame, State, Type, Stream, - PseudoHeaders, Headers, BinLen); - _ -> - stream_reset(StreamID, State, protocol_error, - 'Multiple content-length headers were received. (RFC7230 3.3.2)') - end. - -headers_parse_expected_size(Frame=#headers{id=StreamID}, - State, Type, Stream, PseudoHeaders, Headers, BinLen) -> - try parse_content_length(BinLen) of - Len -> - headers_frame(Frame, State, Type, Stream, PseudoHeaders, Headers, Len) - catch - _:_ -> - HumanReadable = 'The content-length header is invalid. (RFC7230 3.3.2)', - case Type of - request -> headers_malformed(Frame, State, HumanReadable); - response -> stream_reset(StreamID, State, protocol_error, HumanReadable) - end - end. - -headers_frame(#headers{id=StreamID, fin=IsFin}, State0=#http2_machine{ - local_settings=#{initial_window_size := RemoteWindow}, - remote_settings=#{initial_window_size := LocalWindow}}, - Type, Stream0, PseudoHeaders, Headers, Len) -> - {Stream, State1} = case Type of - request -> - TE = case lists:keyfind(<<"te">>, 1, Headers) of - {_, TE0} -> TE0; - false -> undefined - end, - {#stream{id=StreamID, method=maps:get(method, PseudoHeaders), - remote=IsFin, remote_expected_size=Len, - local_window=LocalWindow, remote_window=RemoteWindow, te=TE}, - State0#http2_machine{remote_streamid=StreamID}}; - response -> - Stream1 = case PseudoHeaders of - #{status := Status} when Status >= 100, Status =< 199 -> Stream0; - _ -> Stream0#stream{remote=IsFin, remote_expected_size=Len} - end, - {Stream1, State0} - end, - State = stream_store(Stream, State1), - {ok, {headers, StreamID, IsFin, Headers, PseudoHeaders, Len}, State}. - -trailers_frame(#headers{id=StreamID}, State0, Stream0, Headers) -> - Stream = Stream0#stream{remote=fin}, - State = stream_store(Stream, State0), - case is_body_size_valid(Stream) of - true -> - {ok, {trailers, StreamID, Headers}, State}; - false -> - stream_reset(StreamID, State, protocol_error, - 'The total size of DATA frames is different than the content-length. (RFC7540 8.1.2.6)') - end. - -%% PRIORITY frame. -%% -%% @todo Handle PRIORITY frames. - -priority_frame(_Frame, State) -> - {ok, State}. - -%% RST_STREAM frame. - -rst_stream_frame({rst_stream, StreamID, _}, State=#http2_machine{mode=Mode, - local_streamid=LocalStreamID, remote_streamid=RemoteStreamID}) - when (?IS_LOCAL(Mode, StreamID) andalso (StreamID >= LocalStreamID)) - orelse ((not ?IS_LOCAL(Mode, StreamID)) andalso (StreamID > RemoteStreamID)) -> - {error, {connection_error, protocol_error, - 'RST_STREAM frame received on a stream in idle state. (RFC7540 5.1)'}, - State}; -rst_stream_frame({rst_stream, StreamID, Reason}, State=#http2_machine{streams=Streams0}) -> - Streams = maps:remove(StreamID, Streams0), - State1 = State#http2_machine{streams=Streams, active_stream=undefined}, - {ok, {rst_stream, StreamID, Reason}, remote_stream_linger(StreamID, State1)}. - -%% SETTINGS frame. - -settings_frame({settings, Settings}, State0=#http2_machine{ - opts=Opts, remote_settings=Settings0}) -> - State1 = State0#http2_machine{remote_settings=maps:merge(Settings0, Settings)}, - State2 = maps:fold(fun - (header_table_size, NewSize, State=#http2_machine{encode_state=EncodeState0}) -> - MaxSize = maps:get(max_encode_table_size, Opts, 4096), - EncodeState = hackney_hpack:set_max_size(min(NewSize, MaxSize), EncodeState0), - State#http2_machine{encode_state=EncodeState}; - (initial_window_size, NewWindowSize, State) -> - OldWindowSize = maps:get(initial_window_size, Settings0, 65535), - streams_update_local_window(State, NewWindowSize - OldWindowSize); - (_, _, State) -> - State - end, State1, Settings), - case Settings of - #{initial_window_size := _} -> send_data(State2); - _ -> {ok, State2} - end; -%% We expect to receive a SETTINGS frame as part of the preface. -settings_frame(_F, State=#http2_machine{mode=server}) -> - {error, {connection_error, protocol_error, - 'The preface sequence must be followed by a SETTINGS frame. (RFC7540 3.5)'}, - State}; -settings_frame(_F, State) -> - {error, {connection_error, protocol_error, - 'The preface must begin with a SETTINGS frame. (RFC7540 3.5)'}, - State}. - -%% When SETTINGS_INITIAL_WINDOW_SIZE changes we need to update -%% the local stream windows for all active streams and perhaps -%% resume sending data. -streams_update_local_window(State=#http2_machine{streams=Streams0}, Increment) -> - Streams = maps:map(fun(_, S=#stream{local_window=StreamWindow}) -> - S#stream{local_window=StreamWindow + Increment} - end, Streams0), - State#http2_machine{streams=Streams, active_stream=undefined}. - -%% Ack for a previously sent SETTINGS frame. - -settings_ack_frame(State0=#http2_machine{settings_timer=TRef, - local_settings=Local0, next_settings=NextSettings}) -> - ok = case TRef of - undefined -> ok; - _ -> erlang:cancel_timer(TRef, [{async, true}, {info, false}]) - end, - Local = maps:merge(Local0, NextSettings), - State1 = State0#http2_machine{settings_timer=undefined, - local_settings=Local, next_settings=#{}}, - {ok, maps:fold(fun - (header_table_size, MaxSize, State=#http2_machine{decode_state=DecodeState0}) -> - DecodeState = hackney_hpack:set_max_size(MaxSize, DecodeState0), - State#http2_machine{decode_state=DecodeState}; - (initial_window_size, NewWindowSize, State) -> - OldWindowSize = maps:get(initial_window_size, Local0, 65535), - streams_update_remote_window(State, NewWindowSize - OldWindowSize); - (_, _, State) -> - State - end, State1, NextSettings)}. - -%% When we receive an ack to a SETTINGS frame we sent we need to update -%% the remote stream windows for all active streams. -streams_update_remote_window(State=#http2_machine{streams=Streams0}, Increment) -> - Streams = maps:map(fun(_, S=#stream{remote_window=StreamWindow}) -> - S#stream{remote_window=StreamWindow + Increment} - end, Streams0), - State#http2_machine{streams=Streams, active_stream=undefined}. - -%% PUSH_PROMISE frame. - -%% Convenience record to manipulate the tuple. -%% The order of the fields matter. --record(push_promise, { - id :: hackney_http2:streamid(), - head :: hackney_http2:head_fin(), - promised_id :: hackney_http2:streamid(), - data :: iodata() -}). - -push_promise_frame(_, State=#http2_machine{mode=server}) -> - {error, {connection_error, protocol_error, - 'PUSH_PROMISE frames MUST NOT be sent by the client. (RFC7540 6.6)'}, - State}; -push_promise_frame(_, State=#http2_machine{local_settings=#{enable_push := false}}) -> - {error, {connection_error, protocol_error, - 'PUSH_PROMISE frame received despite SETTINGS_ENABLE_PUSH set to 0. (RFC7540 6.6)'}, - State}; -push_promise_frame(#push_promise{promised_id=PromisedStreamID}, - State=#http2_machine{remote_streamid=RemoteStreamID}) - when PromisedStreamID =< RemoteStreamID -> - {error, {connection_error, protocol_error, - 'PUSH_PROMISE frame received for a promised stream in closed or half-closed state. (RFC7540 5.1, RFC7540 6.6)'}, - State}; -push_promise_frame(#push_promise{id=StreamID}, State) - when not ?IS_CLIENT_LOCAL(StreamID) -> - {error, {connection_error, protocol_error, - 'PUSH_PROMISE frame received on a server-initiated stream. (RFC7540 6.6)'}, - State}; -push_promise_frame(Frame=#push_promise{id=StreamID, head=IsHeadFin, - promised_id=PromisedStreamID, data=HeaderData}, State) -> - case stream_get(StreamID, State) of - Stream=#stream{remote=idle} -> - case IsHeadFin of - head_fin -> - headers_decode(#headers{id=PromisedStreamID, - fin=fin, head=IsHeadFin, data=HeaderData}, - State, push_promise, Stream); - head_nofin -> - {ok, State#http2_machine{state={continuation, push_promise, Frame}}} - end; - _ -> -%% @todo Check if the stream is lingering. If it is, decode the frame -%% and do what? That's the big question and why it's not implemented yet. -% However, an endpoint that -% has sent RST_STREAM on the associated stream MUST handle PUSH_PROMISE -% frames that might have been created before the RST_STREAM frame is -% received and processed. (RFC7540 6.6) - {error, {connection_error, stream_closed, - 'PUSH_PROMISE frame received on a stream in closed or half-closed state. (RFC7540 5.1, RFC7540 6.6)'}, - State} - end. - -push_promise_frame(#headers{id=PromisedStreamID}, - State0=#http2_machine{ - local_settings=#{initial_window_size := RemoteWindow}, - remote_settings=#{initial_window_size := LocalWindow}}, - #stream{id=StreamID}, PseudoHeaders=#{method := Method}, Headers) -> - TE = case lists:keyfind(<<"te">>, 1, Headers) of - {_, TE0} -> TE0; - false -> undefined - end, - PromisedStream = #stream{id=PromisedStreamID, method=Method, - local=fin, local_window=LocalWindow, - remote_window=RemoteWindow, te=TE}, - State = stream_store(PromisedStream, - State0#http2_machine{remote_streamid=PromisedStreamID}), - {ok, {push_promise, StreamID, PromisedStreamID, Headers, PseudoHeaders}, State}. - -%% PING frame. - -ping_frame({ping, _}, State) -> - {ok, State}. - -%% Ack for a previously sent PING frame. -%% -%% @todo Might want to check contents but probably a waste of time. - -ping_ack_frame({ping_ack, _}, State) -> - {ok, State}. - -%% GOAWAY frame. - -goaway_frame(Frame={goaway, _, _, _}, State) -> - {ok, Frame, State}. - -%% WINDOW_UPDATE frame. - -%% Connection-wide WINDOW_UPDATE frame. -window_update_frame({window_update, Increment}, State=#http2_machine{local_window=ConnWindow}) - when ConnWindow + Increment > 16#7fffffff -> - {error, {connection_error, flow_control_error, - 'The flow control window must not be greater than 2^31-1. (RFC7540 6.9.1)'}, - State}; -window_update_frame({window_update, Increment}, State=#http2_machine{local_window=ConnWindow}) -> - send_data(State#http2_machine{local_window=ConnWindow + Increment}); -%% Stream-specific WINDOW_UPDATE frame. -window_update_frame({window_update, StreamID, _}, State=#http2_machine{mode=Mode, - local_streamid=LocalStreamID, remote_streamid=RemoteStreamID}) - when (?IS_LOCAL(Mode, StreamID) andalso (StreamID >= LocalStreamID)) - orelse ((not ?IS_LOCAL(Mode, StreamID)) andalso (StreamID > RemoteStreamID)) -> - {error, {connection_error, protocol_error, - 'WINDOW_UPDATE frame received on a stream in idle state. (RFC7540 5.1)'}, - State}; -window_update_frame({window_update, StreamID, Increment}, - State0=#http2_machine{remote_lingering_streams=Lingering}) -> - case stream_get(StreamID, State0) of - #stream{local_window=StreamWindow} when StreamWindow + Increment > 16#7fffffff -> - stream_reset(StreamID, State0, flow_control_error, - 'The flow control window must not be greater than 2^31-1. (RFC7540 6.9.1)'); - Stream0 = #stream{local_window=StreamWindow} -> - send_data(Stream0#stream{local_window=StreamWindow + Increment}, State0); - undefined -> - %% WINDOW_UPDATE frames may be received for a short period of time - %% after a stream is closed. They must be ignored. - case gb_sets:is_member(StreamID, Lingering) of - false -> {ok, State0}; - true -> stream_reset(StreamID, State0, stream_closed, - 'WINDOW_UPDATE frame received after the stream was reset. (RFC7540 5.1)') - end - end. - -%% CONTINUATION frame. - -%% Convenience record to manipulate the tuple. -%% The order of the fields matter. --record(continuation, { - id :: hackney_http2:streamid(), - head :: hackney_http2:head_fin(), - data :: binary() -}). - -unexpected_continuation_frame(#continuation{}, State) -> - {error, {connection_error, protocol_error, - 'CONTINUATION frames MUST be preceded by a HEADERS or PUSH_PROMISE frame. (RFC7540 6.10)'}, - State}. - -continuation_frame(#continuation{id=StreamID, head=head_fin, data=HeaderFragment1}, - State=#http2_machine{state={continuation, Type, - Frame=#headers{id=StreamID, data=HeaderFragment0}}}) -> - case continuation_frame_append(HeaderFragment0, HeaderFragment1, State) of - {ok, HeaderData} -> - headers_decode(Frame#headers{head=head_fin, data=HeaderData}, - State#http2_machine{state=normal}, Type, stream_get(StreamID, State)); - Error -> - Error - end; -continuation_frame(#continuation{id=StreamID, head=head_fin, data=HeaderFragment1}, - State=#http2_machine{state={continuation, Type, #push_promise{ - id=StreamID, promised_id=PromisedStreamID, data=HeaderFragment0}}}) -> - case continuation_frame_append(HeaderFragment0, HeaderFragment1, State) of - {ok, HeaderData} -> - headers_decode(#headers{id=PromisedStreamID, fin=fin, - head=head_fin, data=HeaderData}, - State#http2_machine{state=normal}, Type, undefined); - Error -> - Error - end; -continuation_frame(#continuation{id=StreamID, data=HeaderFragment1}, - State=#http2_machine{state={continuation, Type, ContinuedFrame}}) - when element(2, ContinuedFrame) =:= StreamID -> - case ContinuedFrame of - #headers{data=HeaderFragment0} -> - case continuation_frame_append(HeaderFragment0, HeaderFragment1, State) of - {ok, HeaderData} -> - {ok, State#http2_machine{state={continuation, Type, - ContinuedFrame#headers{data=HeaderData}}}}; - Error -> - Error - end; - #push_promise{data=HeaderFragment0} -> - case continuation_frame_append(HeaderFragment0, HeaderFragment1, State) of - {ok, HeaderData} -> - {ok, State#http2_machine{state={continuation, Type, - ContinuedFrame#push_promise{data=HeaderData}}}}; - Error -> - Error - end - end; -continuation_frame(_F, State) -> - {error, {connection_error, protocol_error, - 'An invalid frame was received in the middle of a header block. (RFC7540 6.2)'}, - State}. - -%% OPTIMIZATION: Use iolist accumulation to avoid binary copies. -%% The final iolist_to_binary conversion happens in headers_decode. -continuation_frame_append(Fragment0, Fragment1, State=#http2_machine{opts=Opts}) -> - MaxSize = maps:get(max_fragmented_header_block_size, Opts, 32768), - Size0 = iolist_size(Fragment0), - Size1 = byte_size(Fragment1), - case Size0 + Size1 =< MaxSize of - true -> - %% Accumulate as iolist instead of binary concatenation - {ok, [Fragment0, Fragment1]}; - false -> - {error, {connection_error, enhance_your_calm, - 'Larger fragmented header block size than we are willing to accept.'}, - State} - end. - -%% Ignored frames. - --spec ignored_frame(State) - -> {ok, State} - | {error, {connection_error, protocol_error, atom()}, State} - when State::http2_machine(). -ignored_frame(State=#http2_machine{state={continuation, _, _}}) -> - {error, {connection_error, protocol_error, - 'An invalid frame was received in the middle of a header block. (RFC7540 6.2)'}, - State}; -%% @todo It might be useful to error out when we receive -%% too many unknown frames. (RFC7540 10.5) -ignored_frame(State) -> - {ok, State}. - -%% Timeouts. - --spec timeout(preface_timeout | settings_timeout, reference(), State) - -> {ok, State} - | {error, {connection_error, hackney_http2:error(), atom()}, State} - when State::http2_machine(). -timeout(preface_timeout, TRef, State=#http2_machine{preface_timer=TRef}) -> - {error, {connection_error, protocol_error, - 'The preface was not received in a reasonable amount of time.'}, - State}; -timeout(settings_timeout, TRef, State=#http2_machine{settings_timer=TRef}) -> - {error, {connection_error, settings_timeout, - 'The SETTINGS ack was not received within the configured time. (RFC7540 6.5.3)'}, - State}; -timeout(_, _, State) -> - {ok, State}. - -%% Functions for sending a message header or body. Note that -%% this module does not send data directly, instead it returns -%% a value that can then be used to send the frames. - --spec prepare_headers(hackney_http2:streamid(), State, idle | hackney_http2:fin(), - pseudo_headers(), headers()) - -> {ok, hackney_http2:fin(), iodata(), State} when State::http2_machine(). -prepare_headers(StreamID, State=#http2_machine{encode_state=EncodeState0}, - IsFin0, PseudoHeaders, Headers0) -> - Stream = #stream{method=Method, local=idle} = stream_get(StreamID, State), - IsFin = case {IsFin0, Method} of - {idle, _} -> nofin; - {_, <<"HEAD">>} -> fin; - _ -> IsFin0 - end, - Headers = merge_pseudo_headers(PseudoHeaders, remove_http11_headers(Headers0)), - {HeaderBlock, EncodeState} = hackney_hpack:encode(Headers, EncodeState0), - {ok, IsFin, HeaderBlock, stream_store(Stream#stream{local=IsFin0}, - State#http2_machine{encode_state=EncodeState})}. - --spec prepare_push_promise(hackney_http2:streamid(), State, pseudo_headers(), headers()) - -> {ok, hackney_http2:streamid(), iodata(), State} - | {error, no_push} when State::http2_machine(). -prepare_push_promise(_, #http2_machine{remote_settings=#{enable_push := false}}, _, _) -> - {error, no_push}; -prepare_push_promise(StreamID, State=#http2_machine{encode_state=EncodeState0, - local_settings=#{initial_window_size := RemoteWindow}, - remote_settings=#{initial_window_size := LocalWindow}, - local_streamid=LocalStreamID}, PseudoHeaders, Headers0) -> - #stream{local=idle} = stream_get(StreamID, State), - TE = case lists:keyfind(<<"te">>, 1, Headers0) of - {_, TE0} -> TE0; - false -> undefined - end, - Headers = merge_pseudo_headers(PseudoHeaders, remove_http11_headers(Headers0)), - {HeaderBlock, EncodeState} = hackney_hpack:encode(Headers, EncodeState0), - {ok, LocalStreamID, HeaderBlock, stream_store( - #stream{id=LocalStreamID, method=maps:get(method, PseudoHeaders), - remote=fin, remote_expected_size=0, - local_window=LocalWindow, remote_window=RemoteWindow, te=TE}, - State#http2_machine{encode_state=EncodeState, local_streamid=LocalStreamID + 2})}. - -remove_http11_headers(Headers) -> - RemoveHeaders0 = [ - <<"keep-alive">>, - <<"proxy-connection">>, - <<"transfer-encoding">>, - <<"upgrade">> - ], - RemoveHeaders = case lists:keyfind(<<"connection">>, 1, Headers) of - false -> - RemoveHeaders0; - {_, ConnHd} -> - %% We do not need to worry about any "close" header because - %% that header name is reserved. - Connection = parse_connection(ConnHd), - Connection ++ [<<"connection">>|RemoveHeaders0] - end, - lists:filter(fun({Name, _}) -> - not lists:member(Name, RemoveHeaders) - end, Headers). - -merge_pseudo_headers(PseudoHeaders, Headers0) -> - lists:foldl(fun - ({status, Status}, Acc) when is_integer(Status) -> - [{<<":status">>, integer_to_binary(Status)}|Acc]; - ({Name, Value}, Acc) -> - [{iolist_to_binary([$:, atom_to_binary(Name, latin1)]), Value}|Acc] - end, Headers0, maps:to_list(PseudoHeaders)). - --spec prepare_trailers(hackney_http2:streamid(), State, headers()) - -> {ok, iodata(), State} when State::http2_machine(). -prepare_trailers(StreamID, State=#http2_machine{encode_state=EncodeState0}, Trailers) -> - Stream = #stream{local=nofin} = stream_get(StreamID, State), - {HeaderBlock, EncodeState} = hackney_hpack:encode(Trailers, EncodeState0), - {ok, HeaderBlock, stream_store(Stream#stream{local=fin}, - State#http2_machine{encode_state=EncodeState})}. - --spec send_or_queue_data(hackney_http2:streamid(), State, hackney_http2:fin(), DataOrFileOrTrailers) - -> {ok, State} - | {send, [{hackney_http2:streamid(), hackney_http2:fin(), [DataOrFileOrTrailers]}], State} - when State::http2_machine(), DataOrFileOrTrailers:: - {data, iodata()} | #sendfile{} | {trailers, headers()}. -send_or_queue_data(StreamID, State0=#http2_machine{opts=Opts, local_window=ConnWindow}, - IsFin0, DataOrFileOrTrailers0) -> - %% @todo Probably just ignore if the method was HEAD. - Stream0 = #stream{ - local=nofin, - local_window=StreamWindow, - local_buffer_size=BufferSize, - te=TE0 - } = stream_get(StreamID, State0), - DataOrFileOrTrailers = case DataOrFileOrTrailers0 of - {trailers, _} -> - %% We only accept TE headers containing exactly "trailers" (RFC7540 8.1.2.1). - TE = try parse_te(TE0) of - {trailers, []} -> trailers; - _ -> no_trailers - catch _:_ -> - %% If we can't parse the TE header, assume we can't send trailers. - no_trailers - end, - case TE of - trailers -> - DataOrFileOrTrailers0; - no_trailers -> - {data, <<>>} - end; - _ -> - DataOrFileOrTrailers0 - end, - SendSize = case DataOrFileOrTrailers of - {data, D} -> BufferSize + iolist_size(D); - #sendfile{bytes=B} -> BufferSize + B; - {trailers, _} -> 0 - end, - MinSendSize = maps:get(stream_window_data_threshold, Opts, 16384), - if - %% If we cannot send the data all at once and the window - %% is smaller than we are willing to send at a minimum, - %% we queue the data directly. - (StreamWindow < MinSendSize) - andalso ((StreamWindow < SendSize) orelse (ConnWindow < SendSize)) -> - {ok, stream_store(queue_data(Stream0, IsFin0, DataOrFileOrTrailers, in), State0)}; - true -> - case send_or_queue_data(Stream0, State0, [], IsFin0, DataOrFileOrTrailers, in) of - {ok, Stream, State, []} -> - {ok, stream_store(Stream, State)}; - {ok, Stream=#stream{local=IsFin}, State, SendData} -> - {send, [{StreamID, IsFin, lists:reverse(SendData)}], stream_store(Stream, State)} - end - end. - -%% Internal data sending/queuing functions. - -%% @todo Should we ever want to implement the PRIORITY mechanism, -%% this would be the place to do it. Right now, we just go over -%% all streams and send what we can until either everything is -%% sent or we run out of space in the window. -send_data(State0=#http2_machine{streams=Streams0}) -> - Iterator = maps:iterator(Streams0), - case send_data_for_all_streams(maps:next(Iterator), Streams0, State0, []) of - {ok, Streams, State, []} -> - {ok, State#http2_machine{streams=Streams, active_stream=undefined}}; - {ok, Streams, State, Send} -> - {send, Send, State#http2_machine{streams=Streams, active_stream=undefined}} - end. - -send_data_for_all_streams(none, Streams, State, Send) -> - {ok, Streams, State, Send}; -%% While technically we should never get < 0 here, let's be on the safe side. -send_data_for_all_streams(_, Streams, State=#http2_machine{local_window=ConnWindow}, Send) - when ConnWindow =< 0 -> - {ok, Streams, State, Send}; -%% We rely on send_data_for_one_stream/3 to do all the necessary checks about the stream. -send_data_for_all_streams({StreamID, Stream0, Iterator}, Streams, State0, Send) -> - case send_data_for_one_stream(Stream0, State0, []) of - {ok, Stream, State, []} -> - send_data_for_all_streams(maps:next(Iterator), - Streams#{StreamID => Stream}, State, Send); - %% We need to remove the stream here because we do not use stream_store/2. - {ok, #stream{local=fin, remote=fin}, State, SendData} -> - send_data_for_all_streams(maps:next(Iterator), - maps:remove(StreamID, Streams), State, [{StreamID, fin, SendData}|Send]); - {ok, Stream=#stream{local=IsFin}, State, SendData} -> - send_data_for_all_streams(maps:next(Iterator), - Streams#{StreamID => Stream}, State, [{StreamID, IsFin, SendData}|Send]) - end. - -send_data(Stream0, State0) -> - case send_data_for_one_stream(Stream0, State0, []) of - {ok, Stream, State, []} -> - {ok, stream_store(Stream, State)}; - {ok, Stream=#stream{id=StreamID, local=IsFin}, State, SendData} -> - {send, [{StreamID, IsFin, SendData}], stream_store(Stream, State)} - end. - -send_data_for_one_stream(Stream=#stream{local=nofin, local_buffer_size=0, - local_trailers=Trailers}, State, SendAcc) when Trailers =/= undefined -> - {ok, Stream, State, lists:reverse([{trailers, Trailers}|SendAcc])}; -send_data_for_one_stream(Stream=#stream{local=nofin, local_buffer=Q0, local_buffer_size=0}, - State, SendAcc) -> - case queue:len(Q0) of - 0 -> - {ok, Stream, State, lists:reverse(SendAcc)}; - 1 -> - %% We know there is a final empty data frame in the queue. - %% We need to mark the stream as complete. - {{value, {fin, 0, _}}, Q} = queue:out(Q0), - {ok, Stream#stream{local=fin, local_buffer=Q}, State, lists:reverse(SendAcc)} - end; -send_data_for_one_stream(Stream=#stream{local=IsFin, local_window=StreamWindow, - local_buffer_size=BufferSize}, State=#http2_machine{local_window=ConnWindow}, SendAcc) - when ConnWindow =< 0; IsFin =:= fin; StreamWindow =< 0; BufferSize =:= 0 -> - {ok, Stream, State, lists:reverse(SendAcc)}; -send_data_for_one_stream(Stream0=#stream{local_window=StreamWindow, - local_buffer=Q0, local_buffer_size=BufferSize}, - State0=#http2_machine{opts=Opts, local_window=ConnWindow}, SendAcc0) -> - MinSendSize = maps:get(stream_window_data_threshold, Opts, 16384), - if - %% If we cannot send the entire buffer at once and the window - %% is smaller than we are willing to send at a minimum, do nothing. - %% - %% We only do this check the first time we go through this function; - %% we want to send as much data as possible IF we send some. - (SendAcc0 =:= []) andalso (StreamWindow < MinSendSize) - andalso ((StreamWindow < BufferSize) orelse (ConnWindow < BufferSize)) -> - {ok, Stream0, State0, []}; - true -> - %% We know there is an item in the queue. - {{value, {IsFin, DataSize, Data}}, Q} = queue:out(Q0), - Stream1 = Stream0#stream{local_buffer=Q, local_buffer_size=BufferSize - DataSize}, - {ok, Stream, State, SendAcc} - = send_or_queue_data(Stream1, State0, SendAcc0, IsFin, Data, in_r), - send_data_for_one_stream(Stream, State, SendAcc) - end. - -%% We can send trailers immediately if the queue is empty, otherwise we queue. -%% We always send trailer frames even if the window is empty. -send_or_queue_data(Stream=#stream{local_buffer_size=0}, - State, SendAcc, fin, {trailers, Trailers}, _) -> - {ok, Stream, State, [{trailers, Trailers}|SendAcc]}; -send_or_queue_data(Stream, State, SendAcc, fin, {trailers, Trailers}, _) -> - {ok, Stream#stream{local_trailers=Trailers}, State, SendAcc}; -%% Send data immediately if we can, buffer otherwise. -send_or_queue_data(Stream=#stream{local_window=StreamWindow}, - State=#http2_machine{local_window=ConnWindow}, - SendAcc, IsFin, Data, In) - when ConnWindow =< 0; StreamWindow =< 0 -> - {ok, queue_data(Stream, IsFin, Data, In), State, SendAcc}; -send_or_queue_data(Stream=#stream{local_window=StreamWindow}, - State=#http2_machine{opts=Opts, remote_settings=RemoteSettings, - local_window=ConnWindow}, SendAcc, IsFin, Data, In) -> - RemoteMaxFrameSize = maps:get(max_frame_size, RemoteSettings, 16384), - ConfiguredMaxFrameSize = maps:get(max_frame_size_sent, Opts, infinity), - MaxSendSize = min( - min(ConnWindow, StreamWindow), - min(RemoteMaxFrameSize, ConfiguredMaxFrameSize) - ), - case Data of - File = #sendfile{bytes=Bytes} when Bytes =< MaxSendSize -> - {ok, Stream#stream{local=IsFin, local_window=StreamWindow - Bytes}, - State#http2_machine{local_window=ConnWindow - Bytes}, - [File|SendAcc]}; - File = #sendfile{offset=Offset, bytes=Bytes} -> - send_or_queue_data(Stream#stream{local_window=StreamWindow - MaxSendSize}, - State#http2_machine{local_window=ConnWindow - MaxSendSize}, - [File#sendfile{bytes=MaxSendSize}|SendAcc], IsFin, - File#sendfile{offset=Offset + MaxSendSize, bytes=Bytes - MaxSendSize}, In); - {data, Iolist0} -> - IolistSize = iolist_size(Iolist0), - if - IolistSize =< MaxSendSize -> - {ok, Stream#stream{local=IsFin, local_window=StreamWindow - IolistSize}, - State#http2_machine{local_window=ConnWindow - IolistSize}, - [{data, Iolist0}|SendAcc]}; - true -> - {Iolist, More} = iolists_split(MaxSendSize, Iolist0), - send_or_queue_data(Stream#stream{local_window=StreamWindow - MaxSendSize}, - State#http2_machine{local_window=ConnWindow - MaxSendSize}, - [{data, Iolist}|SendAcc], IsFin, {data, More}, In) - end - end. - -queue_data(Stream=#stream{local_buffer=Q0, local_buffer_size=Size0}, IsFin, Data, In) -> - DataSize = case Data of - {sendfile, _, Bytes, _} -> Bytes; - {data, Iolist} -> iolist_size(Iolist) - end, - %% Never queue non-final empty data frames. - case {DataSize, IsFin} of - {0, nofin} -> - Stream; - _ -> - Q = queue:In({IsFin, DataSize, Data}, Q0), - Stream#stream{local_buffer=Q, local_buffer_size=Size0 + DataSize} - end. - -%% Public interface to update the flow control window. -%% -%% The ensure_window function applies heuristics to avoid updating the -%% window when it is not necessary. The update_window function updates -%% the window unconditionally. -%% -%% The ensure_window function should be called when requesting more -%% data (for example when reading a request or response body) as well -%% as when receiving new data. Failure to do so may result in the -%% window being depleted. -%% -%% The heuristics dictating whether the window must be updated and -%% what the window size is depends on three options (margin, max -%% and threshold) along with the Size argument. The window increment -%% returned by this function may therefore be smaller than the Size -%% argument. On the other hand the total window allocated over many -%% calls may end up being larger than the initial Size argument. As -%% a result, it is the responsibility of the caller to ensure that -%% the Size argument is never lower than 0. - --spec ensure_window(non_neg_integer(), State) - -> ok | {ok, pos_integer(), State} when State::http2_machine(). -ensure_window(Size, State=#http2_machine{opts=Opts, remote_window=RemoteWindow}) -> - case ensure_window(Size, RemoteWindow, connection, Opts) of - ok -> - ok; - {ok, Increment} -> - {ok, Increment, State#http2_machine{remote_window=RemoteWindow + Increment}} - end. - --spec ensure_window(hackney_http2:streamid(), non_neg_integer(), State) - -> ok | {ok, pos_integer(), State} when State::http2_machine(). -ensure_window(StreamID, Size, State=#http2_machine{opts=Opts}) -> - case stream_get(StreamID, State) of - %% For simplicity's sake, we do not consider attempts to ensure the window - %% of a terminated stream to be errors. We simply act as if the stream - %% window is large enough. - undefined -> - ok; - Stream = #stream{remote_window=RemoteWindow} -> - case ensure_window(Size, RemoteWindow, stream, Opts) of - ok -> - ok; - {ok, Increment} -> - {ok, Increment, stream_store(Stream#stream{remote_window=RemoteWindow + Increment}, State)} - end - end. - -%% No need to update the window when we are not expecting data. -ensure_window(0, _, _, _) -> - ok; -%% No need to update the window when it is already high enough. -ensure_window(Size, Window, _, _) when Size =< Window -> - ok; -ensure_window(Size0, Window, Type, Opts) -> - Threshold = ensure_window_threshold(Type, Opts), - if - %% We do not update the window when it is higher than the threshold. - Window > Threshold -> - ok; - true -> - Margin = ensure_window_margin(Type, Opts), - Size = Size0 + Margin, - MaxWindow = ensure_window_max(Type, Opts), - Increment = if - %% We cannot go above the maximum window size. - Size > MaxWindow -> MaxWindow - Window; - true -> Size - Window - end, - case Increment of - 0 -> ok; - _ -> {ok, Increment} - end - end. - -%% Margin defaults to the default initial window size. -ensure_window_margin(connection, Opts) -> - maps:get(connection_window_margin_size, Opts, 65535); -ensure_window_margin(stream, Opts) -> - maps:get(stream_window_margin_size, Opts, 65535). - -%% Max window defaults to the max value allowed by the protocol. -ensure_window_max(connection, Opts) -> - maps:get(max_connection_window_size, Opts, 16#7fffffff); -ensure_window_max(stream, Opts) -> - maps:get(max_stream_window_size, Opts, 16#7fffffff). - -%% Threshold defaults to 10 times the default frame size. -ensure_window_threshold(connection, Opts) -> - maps:get(connection_window_update_threshold, Opts, 163840); -ensure_window_threshold(stream, Opts) -> - maps:get(stream_window_update_threshold, Opts, 163840). - --spec update_window(1..16#7fffffff, State) - -> State when State::http2_machine(). -update_window(Size, State=#http2_machine{remote_window=RemoteWindow}) - when Size > 0 -> - State#http2_machine{remote_window=RemoteWindow + Size}. - --spec update_window(hackney_http2:streamid(), 1..16#7fffffff, State) - -> State when State::http2_machine(). -update_window(StreamID, Size, State) - when Size > 0 -> - Stream = #stream{remote_window=RemoteWindow} = stream_get(StreamID, State), - stream_store(Stream#stream{remote_window=RemoteWindow + Size}, State). - -%% Public interface to reset streams. - --spec reset_stream(hackney_http2:streamid(), State) - -> {ok, State} | {error, not_found} when State::http2_machine(). -reset_stream(StreamID, State=#http2_machine{streams=Streams0}) -> - case maps:take(StreamID, Streams0) of - {_, Streams} -> - {ok, local_stream_linger(StreamID, State#http2_machine{streams=Streams, active_stream=undefined})}; - error -> - {error, not_found} - end. - -%% Retrieve the buffer size for all streams. - --spec get_connection_local_buffer_size(http2_machine()) -> non_neg_integer(). -get_connection_local_buffer_size(#http2_machine{streams=Streams}) -> - maps:fold(fun(_, #stream{local_buffer_size=Size}, Acc) -> - Acc + Size - end, 0, Streams). - -%% Retrieve a setting value, or its default value if not set. - --spec get_local_setting(atom(), http2_machine()) -> atom() | integer(). -get_local_setting(Key, #http2_machine{local_settings=Settings}) -> - maps:get(Key, Settings, default_setting_value(Key)). - --spec get_remote_settings(http2_machine()) -> map(). -get_remote_settings(#http2_machine{mode=Mode, remote_settings=Settings}) -> - Defaults0 = #{ - header_table_size => default_setting_value(header_table_size), - enable_push => default_setting_value(enable_push), - max_concurrent_streams => default_setting_value(max_concurrent_streams), - initial_window_size => default_setting_value(initial_window_size), - max_frame_size => default_setting_value(max_frame_size), - max_header_list_size => default_setting_value(max_header_list_size) - }, - Defaults = case Mode of - server -> - Defaults0#{enable_connect_protocol => default_setting_value(enable_connect_protocol)}; - client -> - Defaults0 - end, - maps:merge(Defaults, Settings). - -default_setting_value(header_table_size) -> 4096; -default_setting_value(enable_push) -> true; -default_setting_value(max_concurrent_streams) -> infinity; -default_setting_value(initial_window_size) -> 65535; -default_setting_value(max_frame_size) -> 16384; -default_setting_value(max_header_list_size) -> infinity; -default_setting_value(enable_connect_protocol) -> false. - -%% Function to obtain the last known streamid received -%% for the purposes of sending a GOAWAY frame and closing the connection. - --spec get_last_streamid(http2_machine()) -> hackney_http2:streamid(). -get_last_streamid(#http2_machine{remote_streamid=RemoteStreamID}) -> - RemoteStreamID. - -%% Set last accepted streamid to the last known streamid, for the purpose -%% ignoring frames for remote streams created after sending GOAWAY. - --spec set_last_streamid(http2_machine()) -> {hackney_http2:streamid(), http2_machine()}. -set_last_streamid(State=#http2_machine{remote_streamid=StreamID, - last_remote_streamid=LastStreamID}) when StreamID =< LastStreamID-> - {StreamID, State#http2_machine{last_remote_streamid = StreamID}}. - -%% Retrieve the local buffer size for a stream. - --spec get_stream_local_buffer_size(hackney_http2:streamid(), http2_machine()) - -> {ok, non_neg_integer()} | {error, not_found | closed}. -get_stream_local_buffer_size(StreamID, State=#http2_machine{mode=Mode, - local_streamid=LocalStreamID, remote_streamid=RemoteStreamID}) -> - case stream_get(StreamID, State) of - #stream{local_buffer_size=Size} -> - {ok, Size}; - undefined when (?IS_LOCAL(Mode, StreamID) andalso (StreamID < LocalStreamID)) - orelse ((not ?IS_LOCAL(Mode, StreamID)) andalso (StreamID =< RemoteStreamID)) -> - {error, closed}; - undefined -> - {error, not_found} - end. - -%% Retrieve the local state for a stream, including the state in the queue. - --spec get_stream_local_state(hackney_http2:streamid(), http2_machine()) - -> {ok, idle | hackney_http2:fin(), empty | nofin | fin} | {error, not_found | closed}. -get_stream_local_state(StreamID, State=#http2_machine{mode=Mode, - local_streamid=LocalStreamID, remote_streamid=RemoteStreamID}) -> - case stream_get(StreamID, State) of - #stream{local=IsFin, local_buffer=Q, local_trailers=undefined} -> - IsQueueFin = case queue:peek_r(Q) of - empty -> empty; - {value, {IsQueueFin0, _, _}} -> IsQueueFin0 - end, - {ok, IsFin, IsQueueFin}; - %% Trailers are queued so the local state is fin after the queue is drained. - #stream{local=IsFin} -> - {ok, IsFin, fin}; - undefined when (?IS_LOCAL(Mode, StreamID) andalso (StreamID < LocalStreamID)) - orelse ((not ?IS_LOCAL(Mode, StreamID)) andalso (StreamID =< RemoteStreamID)) -> - {error, closed}; - undefined -> - {error, not_found} - end. - -%% Retrieve the remote state for a stream. - --spec get_stream_remote_state(hackney_http2:streamid(), http2_machine()) - -> {ok, idle | hackney_http2:fin()} | {error, not_found | closed}. -get_stream_remote_state(StreamID, State=#http2_machine{mode=Mode, - local_streamid=LocalStreamID, remote_streamid=RemoteStreamID}) -> - case stream_get(StreamID, State) of - #stream{remote=IsFin} -> - {ok, IsFin}; - undefined when (?IS_LOCAL(Mode, StreamID) andalso (StreamID < LocalStreamID)) - orelse ((not ?IS_LOCAL(Mode, StreamID)) andalso (StreamID =< RemoteStreamID)) -> - {error, closed}; - undefined -> - {error, not_found} - end. - -%% Query whether the stream was reset recently by the remote endpoint. - --spec is_lingering_stream(hackney_http2:streamid(), http2_machine()) -> boolean(). -is_lingering_stream(StreamID, #http2_machine{ - local_lingering_streams=Local, remote_lingering_streams=Remote}) -> - gb_sets:is_member(StreamID, Local) orelse gb_sets:is_member(StreamID, Remote). - -%% Stream-related functions. - -%% OPTIMIZATION: Check cache first before map lookup. -stream_get(StreamID, #http2_machine{active_stream={StreamID, Stream}}) -> - Stream; -stream_get(StreamID, #http2_machine{streams=Streams}) -> - maps:get(StreamID, Streams, undefined). - -%% OPTIMIZATION: Update cache and store stream. -stream_store(#stream{id=StreamID, local=fin, remote=fin}, - State=#http2_machine{streams=Streams0}) -> - Streams = maps:remove(StreamID, Streams0), - State#http2_machine{streams=Streams, active_stream=undefined}; -stream_store(Stream=#stream{id=StreamID}, - State=#http2_machine{streams=Streams}) -> - State#http2_machine{streams=Streams#{StreamID => Stream}, active_stream={StreamID, Stream}}. - -%% @todo Don't send an RST_STREAM if one was already sent. -stream_reset(StreamID, State, Reason, HumanReadable) -> - {error, {stream_error, StreamID, Reason, HumanReadable}, - local_stream_linger(StreamID, State)}. - -%% OPTIMIZATION: Use gb_sets with size limit tracking. -local_stream_linger(StreamID, State=#http2_machine{ - local_lingering_streams=Lingering0, local_lingering_count=Count0}) -> - {Lingering, Count} = case Count0 >= ?MAX_LOCAL_LINGERING of - true -> - %% Remove oldest (smallest) element to make room - {_, Lingering1} = gb_sets:take_smallest(Lingering0), - {gb_sets:add_element(StreamID, Lingering1), Count0}; - false -> - {gb_sets:add_element(StreamID, Lingering0), Count0 + 1} - end, - State#http2_machine{local_lingering_streams=Lingering, - local_lingering_count=Count, active_stream=undefined}. - -remote_stream_linger(StreamID, State=#http2_machine{ - remote_lingering_streams=Lingering0, remote_lingering_count=Count0}) -> - {Lingering, Count} = case Count0 >= ?MAX_REMOTE_LINGERING of - true -> - %% Remove oldest (smallest) element to make room - {_, Lingering1} = gb_sets:take_smallest(Lingering0), - {gb_sets:add_element(StreamID, Lingering1), Count0}; - false -> - {gb_sets:add_element(StreamID, Lingering0), Count0 + 1} - end, - State#http2_machine{remote_lingering_streams=Lingering, remote_lingering_count=Count}. - -%% @private Convert HTTP status binary to integer (replaces cow_http:status_to_integer/1) -status_to_integer(<>) - when $0 =< H, H =< $9, $0 =< T, T =< $9, $0 =< U, U =< $9 -> - (H - $0) * 100 + (T - $0) * 10 + (U - $0); -status_to_integer(<>) - when $0 =< H, H =< $9, $0 =< T, T =< $9, $0 =< U, U =< $9 -> - (H - $0) * 100 + (T - $0) * 10 + (U - $0). - -%%==================================================================== -%% Local helper functions -%% -%% These functions replace dependencies on cow_http_hd and cow_iolists -%% modules from cowlib. They are inlined here to avoid vendoring -%% additional cowlib modules that are not fully needed for HTTP/2 support. -%% -%% Original functions: -%% - cow_http_hd:parse_content_length/1 -%% - cow_http_hd:parse_connection/1 -%% - cow_http_hd:parse_te/1 -%% - cow_iolists:split/2 -%%==================================================================== - -%% @private Parse content-length header -parse_content_length(ContentLength) -> - I = binary_to_integer(ContentLength), - true = I >= 0, - I. - -%% @private Parse connection header -%% Returns list of lowercase connection tokens -parse_connection(<<"close">>) -> - [<<"close">>]; -parse_connection(<<"keep-alive">>) -> - [<<"keep-alive">>]; -parse_connection(Connection) -> - parse_token_ci_list(Connection, []). - -parse_token_ci_list(<<>>, Acc) -> - lists:reverse(Acc); -parse_token_ci_list(<>, Acc) when C =:= $\s; C =:= $\t; C =:= $, -> - parse_token_ci_list(R, Acc); -parse_token_ci_list(<>, Acc) -> - parse_token_ci(R, Acc, <<(char_to_lower(C))>>). - -parse_token_ci(<<>>, Acc, T) -> - lists:reverse([T|Acc]); -parse_token_ci(<>, Acc, T) when C =:= $\s; C =:= $\t; C =:= $, -> - parse_token_ci_list(R, [T|Acc]); -parse_token_ci(<>, Acc, T) -> - parse_token_ci(R, Acc, <>). - -char_to_lower(C) when C >= $A, C =< $Z -> C + 32; -char_to_lower(C) -> C. - -%% @private Parse TE header (simplified version) -%% Returns {trailers | no_trailers, []} -parse_te(TE) -> - case binary:match(TE, <<"trailers">>) of - {_, _} -> {trailers, []}; - nomatch -> {no_trailers, []} - end. - -%% @private Split iodata at position N -iolists_split(N, Iolist) -> - case iolists_split(N, Iolist, []) of - {ok, Before, After} -> - {Before, After}; - {more, _, Before} -> - {lists:reverse(Before), <<>>} - end. - -iolists_split(0, Rest, Acc) -> - {ok, lists:reverse(Acc), Rest}; -iolists_split(N, [], Acc) -> - {more, N, Acc}; -iolists_split(N, Binary, Acc) when is_binary(Binary), byte_size(Binary) =< N -> - {more, N - byte_size(Binary), [Binary|Acc]}; -iolists_split(N, Binary, Acc) when is_binary(Binary) -> - << Before:N/binary, After/bits >> = Binary, - {ok, lists:reverse([Before|Acc]), After}; -iolists_split(N, [Binary|Tail], Acc) when is_binary(Binary), byte_size(Binary) =< N -> - iolists_split(N - byte_size(Binary), Tail, [Binary|Acc]); -iolists_split(N, [Binary|Tail], Acc) when is_binary(Binary) -> - << Before:N/binary, After/bits >> = Binary, - {ok, lists:reverse([Before|Acc]), [After|Tail]}; -iolists_split(N, [Char|Tail], Acc) when is_integer(Char) -> - iolists_split(N - 1, Tail, [Char|Acc]); -iolists_split(N, [List|Tail], Acc0) -> - case iolists_split(N, List, Acc0) of - {ok, Before, After} -> - {ok, Before, [After|Tail]}; - {more, More, Acc} -> - iolists_split(More, Tail, Acc) - end. diff --git a/test/h2spec_SUITE.erl b/test/h2spec_SUITE.erl deleted file mode 100644 index e53d4fc4..00000000 --- a/test/h2spec_SUITE.erl +++ /dev/null @@ -1,237 +0,0 @@ -%%% -*- erlang -*- -%%% -%%% This file is part of hackney released under the Apache 2 license. -%%% See the NOTICE for more information. -%%% -%%% Copyright (c) 2024-2026 Benoit Chesneau -%%% -%%% @doc h2spec compliance test suite for HTTP/2 implementation. -%%% -%%% This suite runs h2spec tests against hackney's HTTP/2 implementation. -%%% h2spec must be installed and available in PATH, or downloaded via make. -%%% -%%% See: https://github.com/summerwind/h2spec -%%% -%%% To run these tests: -%%% make h2spec-test -%%% or: -%%% rebar3 ct --suite=h2spec_SUITE - --module(h2spec_SUITE). - --include_lib("common_test/include/ct.hrl"). - --export([ - all/0, - groups/0, - init_per_suite/1, - end_per_suite/1, - init_per_testcase/2, - end_per_testcase/2 -]). - --export([ - h2spec_generic/1, - h2spec_hpack/1, - h2spec_http2/1 -]). - --define(H2SPEC_PORT, 18443). --define(H2SPEC_TIMEOUT, 120000). - -%%==================================================================== -%% CT Callbacks -%%==================================================================== - -all() -> - [{group, h2spec_tests}]. - -groups() -> - [{h2spec_tests, [sequence], [ - h2spec_generic, - h2spec_hpack, - h2spec_http2 - ]}]. - -init_per_suite(Config) -> - %% Check if h2spec is available - H2SpecPath = find_h2spec(), - case H2SpecPath of - false -> - {skip, "h2spec binary not found. Run 'make download-h2spec' to install."}; - Path -> - %% Ensure SSL application is started - application:ensure_all_started(ssl), - application:ensure_all_started(hackney), - - %% Generate test certificates if needed - case ensure_test_certs() of - ok -> - %% Start h2spec server - case h2spec_server:start(?H2SPEC_PORT) of - {ok, ServerPid} -> - %% Give the accept loop time to start - timer:sleep(500), - ct:log("Server started, Pid: ~p~n", [ServerPid]), - [{h2spec_path, Path}, - {h2spec_port, ?H2SPEC_PORT}, - {server_pid, ServerPid} | Config]; - {error, Reason} -> - ct:fail({server_start_failed, Reason}) - end; - {error, CertError} -> - {skip, {no_test_certs, CertError}} - end - end. - -end_per_suite(Config) -> - case proplists:get_value(server_pid, Config) of - undefined -> ok; - Pid -> h2spec_server:stop(Pid) - end, - ok. - -init_per_testcase(_TestCase, Config) -> - Config. - -end_per_testcase(_TestCase, _Config) -> - ok. - -%%==================================================================== -%% Test Cases -%%==================================================================== - -%% @doc Run h2spec generic tests (connection preface, framing). -h2spec_generic(Config) -> - run_h2spec(Config, ["generic"]). - -%% @doc Run h2spec HPACK tests. -h2spec_hpack(Config) -> - run_h2spec(Config, ["hpack"]). - -%% @doc Run h2spec HTTP/2 tests. -h2spec_http2(Config) -> - run_h2spec(Config, ["http2"]). - -%%==================================================================== -%% Internal Functions -%%==================================================================== - -run_h2spec(Config, Sections) -> - H2SpecPath = proplists:get_value(h2spec_path, Config), - Port = proplists:get_value(h2spec_port, Config), - - %% Build h2spec command - Args = [ - "-p", integer_to_list(Port), - "-t", %% TLS mode - "-k", %% Skip TLS verification - "--strict", - "-j", "/tmp/h2spec-report.json" - ] ++ Sections, - - Cmd = H2SpecPath ++ " " ++ string:join(Args, " "), - ct:log("Running: ~s", [Cmd]), - - %% Run h2spec - Result = os:cmd(Cmd), - ct:log("h2spec output:~n~s", [Result]), - - %% Check if tests passed - accept > 90% pass rate - case parse_h2spec_results(Result) of - {ok, Total, Passed, _Skipped, _Failed} when Total > 0 -> - PassRate = Passed * 100 / Total, - ct:log("h2spec pass rate: ~.1f% (~p/~p)", [PassRate, Passed, Total]), - if - PassRate >= 90.0 -> - ok; - true -> - ct:fail({h2spec_pass_rate_too_low, PassRate, Result}) - end; - {ok, 0, 0, _, _} -> - %% No tests run - likely a connection issue - ct:fail({h2spec_no_tests_run, Result}); - error -> - %% Couldn't parse results - check for known success patterns - case string:find(Result, "All tests passed") of - nomatch -> - case string:find(Result, "0 failed") of - nomatch -> - ct:fail({h2spec_failed, Result}); - _ -> - ok - end; - _ -> - ok - end - end. - -%% @private Parse h2spec output to extract test counts. -%% Output format: "X tests, Y passed, Z skipped, N failed" -parse_h2spec_results(Output) -> - %% Convert to binary and handle Unicode properly - OutputBin = case Output of - Bin when is_binary(Bin) -> Bin; - List when is_list(List) -> unicode:characters_to_binary(List) - end, - %% Look for pattern like "146 tests, 139 passed, 1 skipped, 6 failed" - case re:run(OutputBin, "([0-9]+) tests, ([0-9]+) passed, ([0-9]+) skipped, ([0-9]+) failed", - [{capture, all_but_first, list}]) of - {match, [Total, Passed, Skipped, Failed]} -> - {ok, list_to_integer(Total), list_to_integer(Passed), - list_to_integer(Skipped), list_to_integer(Failed)}; - nomatch -> - error - end. - -%% @private Find h2spec binary in PATH or priv directory. -find_h2spec() -> - %% Check priv directory first - PrivPath = filename:join([code:priv_dir(hackney), "h2spec"]), - case filelib:is_regular(PrivPath) of - true -> - PrivPath; - false -> - %% Check PATH - case os:find_executable("h2spec") of - false -> false; - Path -> Path - end - end. - -%% @private Ensure test certificates exist. -ensure_test_certs() -> - CertDir = filename:join([code:priv_dir(hackney), "test_certs"]), - CertFile = filename:join(CertDir, "server.pem"), - KeyFile = filename:join(CertDir, "server.key"), - - case {filelib:is_regular(CertFile), filelib:is_regular(KeyFile)} of - {true, true} -> - ok; - _ -> - %% Try to generate certificates using OpenSSL - case generate_test_certs(CertDir, CertFile, KeyFile) of - ok -> ok; - Error -> {error, Error} - end - end. - -generate_test_certs(_CertDir, CertFile, KeyFile) -> - ok = filelib:ensure_dir(CertFile), - - %% Generate self-signed certificate - Cmd = io_lib:format( - "openssl req -x509 -newkey rsa:2048 -keyout ~s -out ~s " - "-days 365 -nodes -subj '/CN=localhost' 2>/dev/null", - [KeyFile, CertFile]), - - case os:cmd(lists:flatten(Cmd)) of - [] -> - case {filelib:is_regular(CertFile), filelib:is_regular(KeyFile)} of - {true, true} -> ok; - _ -> {error, cert_generation_failed} - end; - Error -> - {error, {openssl_error, Error}} - end. - diff --git a/test/h2spec_server.erl b/test/h2spec_server.erl deleted file mode 100644 index 6f627b54..00000000 --- a/test/h2spec_server.erl +++ /dev/null @@ -1,313 +0,0 @@ -%%% -*- erlang -*- -%%% -%%% This file is part of hackney released under the Apache 2 license. -%%% See the NOTICE for more information. -%%% -%%% Copyright (c) 2024-2026 Benoit Chesneau -%%% -%%% @doc Minimal HTTP/2 server for h2spec compliance testing. -%%% -%%% This server implements just enough of HTTP/2 to pass h2spec tests. -%%% It handles the connection preface, SETTINGS frames, and responds -%%% to simple GET requests. - --module(h2spec_server). - --export([start/1, start/2, stop/1]). --export([accept_loop/2]). - --define(ALPN_PROTOCOLS, [<<"h2">>]). --define(DEFAULT_MAX_FRAME_SIZE, 16384). - -%% @doc Start the h2spec test server on the given port. -%% Returns {ok, ListenSocket} on success. --spec start(inet:port_number()) -> {ok, ssl:sslsocket()} | {error, term()}. -start(Port) -> - start(Port, #{}). - --spec start(inet:port_number(), map()) -> {ok, pid()} | {error, term()}. -start(Port, _Opts) -> - CertFile = cert_file(), - KeyFile = key_file(), - case {filelib:is_regular(CertFile), filelib:is_regular(KeyFile)} of - {true, true} -> - %% Spawn a server process that owns the listen socket - %% This ensures the socket stays open even if the calling process exits - Self = self(), - Pid = spawn(fun() -> server_process(Port, CertFile, KeyFile, Self) end), - receive - {Pid, started} -> - {ok, Pid}; - {Pid, {error, Reason}} -> - {error, Reason} - after 5000 -> - {error, timeout} - end; - _ -> - {error, {missing_certs, CertFile, KeyFile}} - end. - -server_process(Port, CertFile, KeyFile, Caller) -> - ListenOpts = [ - binary, - {active, false}, - {reuseaddr, true}, - {alpn_preferred_protocols, ?ALPN_PROTOCOLS}, - {certfile, CertFile}, - {keyfile, KeyFile}, - {versions, ['tlsv1.2', 'tlsv1.3']} - ], - case ssl:listen(Port, ListenOpts) of - {ok, LSock} -> - Caller ! {self(), started}, - %% Enter accept loop directly (this process owns the socket) - accept_loop(LSock, Caller); - {error, Reason} -> - Caller ! {self(), {error, Reason}} - end. - -%% @doc Stop the server by killing the server process. --spec stop(pid()) -> ok. -stop(Pid) when is_pid(Pid) -> - exit(Pid, shutdown), - ok; -stop(LSock) -> - %% Backward compatibility: allow passing socket directly - ssl:close(LSock). - -%% @private Accept loop - spawns a handler for each connection. -accept_loop(LSock, Parent) -> - case ssl:transport_accept(LSock, 5000) of - {ok, Socket} -> - case ssl:handshake(Socket, 5000) of - {ok, SSLSocket} -> - %% Verify ALPN negotiated h2 - case ssl:negotiated_protocol(SSLSocket) of - {ok, <<"h2">>} -> - %% Transfer socket ownership to new process - Pid = spawn(fun() -> - receive {socket, S} -> connection_loop(S) end - end), - ssl:controlling_process(SSLSocket, Pid), - Pid ! {socket, SSLSocket}; - _ -> - ssl:close(SSLSocket) - end; - {error, _} -> - ssl:close(Socket) - end, - accept_loop(LSock, Parent); - {error, timeout} -> - accept_loop(LSock, Parent); - {error, closed} -> - ok; - {error, _Reason} -> - accept_loop(LSock, Parent) - end. - -%% @private Connection handler - implements HTTP/2 server side. -connection_loop(Socket) -> - %% Initialize HTTP/2 machine in server mode - Opts = #{preface_timeout => infinity, settings_timeout => infinity}, - {ok, Preface, H2Machine0} = hackney_http2_machine:init(server, Opts), - - %% Send server preface (SETTINGS frame) - case ssl:send(Socket, Preface) of - ok -> - %% Set socket to active once to receive data - ok = ssl:setopts(Socket, [{active, once}]), - %% Enter preface loop first (to consume client magic string) - preface_loop(Socket, H2Machine0, <<>>); - {error, closed} -> - %% Client closed connection before we could send preface - ok; - {error, _Reason} -> - ssl:close(Socket) - end. - -%% @private Wait for client preface magic string before frame processing -preface_loop(Socket, H2Machine, Buffer) -> - receive - {ssl, Socket, Data} -> - NewBuffer = <>, - case hackney_http2:parse_sequence(NewBuffer) of - {ok, Rest} -> - %% Now process any remaining data as frames - ok = ssl:setopts(Socket, [{active, once}]), - case process_frames(Socket, H2Machine, Rest) of - {ok, H2Machine1, Rest1} -> - frame_loop(Socket, H2Machine1, Rest1); - {error, _Reason} -> - ssl:close(Socket) - end; - more -> - ok = ssl:setopts(Socket, [{active, once}]), - preface_loop(Socket, H2Machine, NewBuffer); - {connection_error, ErrorCode, _Msg} -> - send_goaway(Socket, 0, ErrorCode), - ssl:close(Socket) - end; - {ssl_closed, Socket} -> - ok; - {ssl_error, Socket, _Reason} -> - ssl:close(Socket) - after 30000 -> - ssl:close(Socket) - end. - -frame_loop(Socket, H2Machine, Buffer) -> - receive - {ssl, Socket, Data} -> - NewBuffer = <>, - case process_frames(Socket, H2Machine, NewBuffer) of - {ok, H2Machine1, Rest} -> - ok = ssl:setopts(Socket, [{active, once}]), - frame_loop(Socket, H2Machine1, Rest); - {error, _Reason} -> - ssl:close(Socket) - end; - {ssl_closed, Socket} -> - ok; - {ssl_error, Socket, _Reason} -> - ssl:close(Socket) - after 30000 -> - ssl:close(Socket) - end. - -process_frames(Socket, H2Machine, Buffer) -> - case hackney_http2:parse(Buffer, ?DEFAULT_MAX_FRAME_SIZE) of - {ok, Frame, Rest} -> - case handle_frame(Socket, H2Machine, Frame) of - {ok, H2Machine1} -> - %% Handle frames that require immediate response - H2Machine2 = case Frame of - {settings, _Settings} -> - SettingsAck = hackney_http2:settings_ack(), - ssl:send(Socket, SettingsAck), - H2Machine1; - {ping, Opaque} -> - PingAck = hackney_http2:ping_ack(Opaque), - ssl:send(Socket, PingAck), - H2Machine1; - _ -> - H2Machine1 - end, - process_frames(Socket, H2Machine2, Rest); - {error, Reason} -> - {error, Reason} - end; - more -> - {ok, H2Machine, Buffer}; - {ignore, Rest} -> - %% Unknown frame types MUST be ignored per RFC 7540 4.1 - process_frames(Socket, H2Machine, Rest); - {connection_error, ErrorCode, _Msg} -> - send_goaway(Socket, 0, ErrorCode), - {error, ErrorCode}; - {stream_error, StreamId, ErrorCode, _, Rest} -> - send_rst_stream(Socket, StreamId, ErrorCode), - process_frames(Socket, H2Machine, Rest) - end. - -handle_frame(Socket, H2Machine, Frame) -> - case hackney_http2_machine:frame(Frame, H2Machine) of - {ok, H2Machine1} -> - %% No response needed (e.g., SETTINGS ACK, PING ACK, PRIORITY) - {ok, H2Machine1}; - {ok, {headers, StreamId, IsFin, Headers, PseudoHeaders, _Len}, H2Machine1} -> - %% Incoming request - send response - handle_request(Socket, H2Machine1, StreamId, IsFin, Headers, PseudoHeaders); - {ok, {data, _StreamId, _IsFin, _Data}, H2Machine1} -> - %% Request body data - just acknowledge - {ok, H2Machine1}; - {ok, {rst_stream, _StreamId, _Reason}, H2Machine1} -> - {ok, H2Machine1}; - {ok, {goaway, _LastStreamId, _ErrorCode, _DebugData}, H2Machine1} -> - {ok, H2Machine1}; - {ok, {ping, Opaque}, H2Machine1} -> - %% Send PING ACK - PingAck = hackney_http2:ping_ack(Opaque), - ok = ssl:send(Socket, PingAck), - {ok, H2Machine1}; - {ok, {trailers, _StreamId, _Trailers}, H2Machine1} -> - {ok, H2Machine1}; - {ok, {window_update, _}, H2Machine1} -> - %% Window update processed - {ok, H2Machine1}; - {ok, {settings, _Settings}, H2Machine1} -> - %% Settings received, ACK should be sent automatically by machine - {ok, H2Machine1}; - {ok, {settings_ack}, H2Machine1} -> - %% Settings ACK received - {ok, H2Machine1}; - {ok, {priority, _StreamId, _Exclusive, _DepStreamId, _Weight}, H2Machine1} -> - %% Priority frame - just acknowledge - {ok, H2Machine1}; - {ok, Other, H2Machine1} -> - %% Log unexpected but handle gracefully - error_logger:info_msg("h2spec_server: unhandled frame result: ~p~n", [Other]), - {ok, H2Machine1}; - {send, SendData, H2Machine1} -> - %% Send queued data (e.g., SETTINGS_ACK, WINDOW_UPDATE) - lists:foreach(fun({_StreamId, _Fin, DataList}) -> - lists:foreach(fun - ({data, D}) -> ssl:send(Socket, D); - ({trailers, _T}) -> ok - end, DataList) - end, SendData), - {ok, H2Machine1}; - {send, Data, _Event, H2Machine1} when is_binary(Data); is_list(Data) -> - %% Direct send data (SETTINGS_ACK, etc.) - ok = ssl:send(Socket, Data), - {ok, H2Machine1}; - {error, {connection_error, ErrorCode, _Msg}, _H2Machine1} -> - send_goaway(Socket, 0, ErrorCode), - {error, ErrorCode}; - {error, {stream_error, StreamId, ErrorCode, _Msg}, H2Machine1} -> - send_rst_stream(Socket, StreamId, ErrorCode), - {ok, H2Machine1}; - Other -> - %% Catch-all for any other return values - error_logger:warning_msg("h2spec_server: unexpected frame/2 return: ~p~n", [Other]), - {error, {unexpected_return, Other}} - end. - -handle_request(Socket, H2Machine0, StreamId, _IsFin, _Headers, PseudoHeaders) -> - %% Simple response - 200 OK with body - Method = maps:get(method, PseudoHeaders, <<"GET">>), - Path = maps:get(path, PseudoHeaders, <<"/">>), - - %% Prepare response - ResponseHeaders = [{<<"content-type">>, <<"text/plain">>}], - ResponseBody = <<"Hello from h2spec test server\r\nMethod: ", - Method/binary, "\r\nPath: ", Path/binary>>, - - %% Prepare and send headers - {ok, _RespIsFin, HeaderBlock, H2Machine1} = hackney_http2_machine:prepare_headers( - StreamId, H2Machine0, nofin, - #{status => 200}, ResponseHeaders), - - HeadersFrame = hackney_http2:headers(StreamId, nofin, HeaderBlock), - ok = ssl:send(Socket, HeadersFrame), - - %% Send body as DATA frame with END_STREAM - DataFrame = hackney_http2:data(StreamId, fin, ResponseBody), - ok = ssl:send(Socket, DataFrame), - - {ok, H2Machine1}. - -send_goaway(Socket, LastStreamId, ErrorCode) -> - Frame = hackney_http2:goaway(LastStreamId, ErrorCode, <<>>), - ssl:send(Socket, Frame). - -send_rst_stream(Socket, StreamId, ErrorCode) -> - Frame = hackney_http2:rst_stream(StreamId, ErrorCode), - ssl:send(Socket, Frame). - -%% @private Path to test certificate file. -cert_file() -> - filename:join([code:priv_dir(hackney), "test_certs", "server.pem"]). - -%% @private Path to test key file. -key_file() -> - filename:join([code:priv_dir(hackney), "test_certs", "server.key"]). diff --git a/test/hackney_conn_http2_tests.erl b/test/hackney_conn_http2_tests.erl deleted file mode 100644 index 10c27b93..00000000 --- a/test/hackney_conn_http2_tests.erl +++ /dev/null @@ -1,103 +0,0 @@ -%%% -*- erlang -*- -%%% -%%% This file is part of hackney released under the Apache 2 license. -%%% See the NOTICE for more information. -%%% -%%% Copyright (c) 2024-2026 Benoit Chesneau -%%% -%%% @doc Tests for HTTP/2 connection support in hackney_conn. -%%% -%%% Note: Integration tests depend on nghttp2.org and gracefully skip -%%% if network is unavailable or connections fail. - --module(hackney_conn_http2_tests). - --include_lib("eunit/include/eunit.hrl"). - -%%==================================================================== -%% Test Setup -%%==================================================================== - -setup() -> - {ok, _} = application:ensure_all_started(hackney), - ok. - -cleanup(_) -> - hackney_conn_sup:stop_all(), - ok. - -%%==================================================================== -%% Protocol Detection Tests (Unit tests - no network required) -%%==================================================================== - -%% Test that TCP connections default to http1 protocol -tcp_connection_defaults_to_http1_test() -> - setup(), - {ok, Pid} = hackney_conn:start_link(#{ - host => "localhost", - port => 8080, - transport => hackney_tcp - }), - ?assertEqual(http1, hackney_conn:get_protocol(Pid)), - hackney_conn:stop(Pid). - -%% Test get_protocol API on idle connection -get_protocol_idle_test() -> - setup(), - {ok, Pid} = hackney_conn:start_link(#{ - host => "example.com", - port => 443, - transport => hackney_ssl - }), - ?assertEqual(http1, hackney_conn:get_protocol(Pid)), - hackney_conn:stop(Pid). - -%%==================================================================== -%% HTTP/2 Machine Initialization Tests (Unit tests) -%%==================================================================== - -h2_machine_init_test() -> - Opts = #{preface_timeout => infinity, settings_timeout => infinity}, - {ok, Preface, Machine} = hackney_http2_machine:init(client, Opts), - ?assert(is_binary(iolist_to_binary(Preface))), - ?assert(is_tuple(Machine)), - PrefaceBin = iolist_to_binary(Preface), - ?assertMatch(<<"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", _/binary>>, PrefaceBin). - -%%==================================================================== -%% ALPN Options Tests (Unit tests) -%%==================================================================== - -alpn_opts_default_test() -> - Opts = hackney_ssl:alpn_opts([]), - ?assertEqual([{alpn_advertised_protocols, [<<"h2">>, <<"http/1.1">>]}], Opts). - -alpn_opts_http2_only_test() -> - Opts = hackney_ssl:alpn_opts([{protocols, [http2]}]), - ?assertEqual([{alpn_advertised_protocols, [<<"h2">>]}], Opts). - -alpn_opts_http1_only_test() -> - Opts = hackney_ssl:alpn_opts([{protocols, [http1]}]), - ?assertEqual([{alpn_advertised_protocols, [<<"http/1.1">>]}], Opts). - -%%==================================================================== -%% Test Suites -%%==================================================================== - -protocol_detection_test_() -> - [ - {"TCP defaults to HTTP/1", fun tcp_connection_defaults_to_http1_test/0}, - {"Get protocol on idle", fun get_protocol_idle_test/0} - ]. - -h2_machine_test_() -> - [ - {"H2 machine init", fun h2_machine_init_test/0} - ]. - -alpn_test_() -> - [ - {"ALPN opts default", fun alpn_opts_default_test/0}, - {"ALPN opts HTTP/2 only", fun alpn_opts_http2_only_test/0}, - {"ALPN opts HTTP/1 only", fun alpn_opts_http1_only_test/0} - ]. diff --git a/test/hackney_hpack_bench.erl b/test/hackney_hpack_bench.erl deleted file mode 100644 index 230ad3c3..00000000 --- a/test/hackney_hpack_bench.erl +++ /dev/null @@ -1,211 +0,0 @@ -%%% -*- erlang -*- -%%% -%%% This file is part of hackney released under the Apache 2 license. -%%% See the NOTICE for more information. -%%% -%%% Copyright (c) 2024-2026 Benoit Chesneau -%%% -%%% @doc HPACK benchmark suite. -%%% -%%% Performance benchmarks for hackney_hpack module. -%%% @end - --module(hackney_hpack_bench). - --export([run/0, run/1]). --export([bench_encode/0, bench_decode/0, bench_dynamic_table/0]). - --define(DEFAULT_ITERATIONS, 10000). - -%% Sample headers for benchmarking --define(SIMPLE_HEADERS, [ - {<<":method">>, <<"GET">>}, - {<<":path">>, <<"/">>}, - {<<":scheme">>, <<"https">>}, - {<<":authority">>, <<"example.com">>} -]). - --define(TYPICAL_REQUEST, [ - {<<":method">>, <<"GET">>}, - {<<":path">>, <<"/api/v1/users">>}, - {<<":scheme">>, <<"https">>}, - {<<":authority">>, <<"api.example.com">>}, - {<<"user-agent">>, <<"Mozilla/5.0">>}, - {<<"accept">>, <<"application/json">>}, - {<<"accept-encoding">>, <<"gzip, deflate">>}, - {<<"authorization">>, <<"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9">>}, - {<<"x-request-id">>, <<"550e8400-e29b-41d4-a716-446655440000">>} -]). - --define(TYPICAL_RESPONSE, [ - {<<":status">>, <<"200">>}, - {<<"content-type">>, <<"application/json">>}, - {<<"content-length">>, <<"1234">>}, - {<<"cache-control">>, <<"max-age=3600">>}, - {<<"etag">>, <<"\"abc123\"">>}, - {<<"date">>, <<"Fri, 21 Feb 2025 12:00:00 GMT">>}, - {<<"server">>, <<"hackney">>}, - {<<"x-request-id">>, <<"550e8400-e29b-41d4-a716-446655440000">>} -]). - -%%==================================================================== -%% Public API -%%==================================================================== - -run() -> - run(?DEFAULT_ITERATIONS). - -run(N) -> - io:format("~n=== HPACK Benchmark Suite ===~n"), - io:format("Iterations: ~p~n~n", [N]), - - %% Warm up JIT - io:format("Warming up...~n"), - _ = [bench_iteration(encode, ?SIMPLE_HEADERS, hackney_hpack, hackney_hpack:init()) || _ <- lists:seq(1, 1000)], - - io:format("~n--- Encode Benchmarks ---~n~n"), - bench_encode_single(N, "Simple headers (4)", ?SIMPLE_HEADERS), - bench_encode_single(N, "Typical request (9)", ?TYPICAL_REQUEST), - bench_encode_single(N, "Typical response (8)", ?TYPICAL_RESPONSE), - - io:format("~n--- Decode Benchmarks ---~n~n"), - bench_decode_single(N, "Simple headers (4)", ?SIMPLE_HEADERS), - bench_decode_single(N, "Typical request (9)", ?TYPICAL_REQUEST), - bench_decode_single(N, "Typical response (8)", ?TYPICAL_RESPONSE), - - io:format("~n--- Dynamic Table Lookup ---~n~n"), - bench_dynamic_table_single(N), - - io:format("~n=== Benchmark Complete ===~n"), - ok. - -%%==================================================================== -%% Individual Benchmarks -%%==================================================================== - -bench_encode() -> - io:format("~n--- Encode Performance ---~n"), - - State = hackney_hpack:init(), - - {Time, _} = timer:tc(fun() -> - lists:foldl(fun(_, S) -> - {_, S2} = hackney_hpack:encode(?TYPICAL_REQUEST, S), - S2 - end, State, lists:seq(1, 10000)) - end), - - io:format("hackney_hpack: ~.2f ms (~.2f us/op)~n", [Time / 1000, Time / 10000]), - ok. - -bench_decode() -> - io:format("~n--- Decode Performance ---~n"), - - {Encoded, _} = hackney_hpack:encode(?TYPICAL_REQUEST), - EncodedBin = iolist_to_binary(Encoded), - - State = hackney_hpack:init(), - - {Time, _} = timer:tc(fun() -> - lists:foldl(fun(_, S) -> - {_, S2} = hackney_hpack:decode(EncodedBin, S), - S2 - end, State, lists:seq(1, 10000)) - end), - - io:format("hackney_hpack: ~.2f ms (~.2f us/op)~n", [Time / 1000, Time / 10000]), - ok. - -bench_dynamic_table() -> - io:format("~n--- Dynamic Table Lookup Performance ---~n"), - io:format("(After inserting 50 unique headers)~n"), - - %% Generate unique headers to fill dynamic table - Headers = [{<<"x-custom-", (integer_to_binary(I))/binary>>, - <<"value-", (integer_to_binary(I))/binary>>} - || I <- lists:seq(1, 50)], - - %% Build up state with entries - State = lists:foldl(fun(H, S) -> - {_, S2} = hackney_hpack:encode([H], S), - S2 - end, hackney_hpack:init(), Headers), - - %% Now benchmark lookups - TestHeader = [{<<"x-custom-25">>, <<"value-25">>}], - - {Time, _} = timer:tc(fun() -> - lists:foreach(fun(_) -> - hackney_hpack:encode(TestHeader, State) - end, lists:seq(1, 100000)) - end), - - io:format("hackney_hpack O(1) map lookup: ~.2f ms (~.2f us/op)~n", - [Time / 1000, Time / 100000]), - ok. - -%%==================================================================== -%% Internal Functions -%%==================================================================== - -bench_encode_single(N, Label, Headers) -> - State = hackney_hpack:init(), - - {Time, _} = timer:tc(fun() -> - lists:foldl(fun(_, S) -> - {_, S2} = hackney_hpack:encode(Headers, S), - S2 - end, State, lists:seq(1, N)) - end), - - io:format("~s:~n", [Label]), - io:format(" hackney_hpack: ~.2f us/op~n~n", [Time / N]). - -bench_decode_single(N, Label, Headers) -> - {Encoded, _} = hackney_hpack:encode(Headers), - EncodedBin = iolist_to_binary(Encoded), - - State = hackney_hpack:init(), - - {Time, _} = timer:tc(fun() -> - lists:foldl(fun(_, S) -> - {_, S2} = hackney_hpack:decode(EncodedBin, S), - S2 - end, State, lists:seq(1, N)) - end), - - io:format("~s:~n", [Label]), - io:format(" hackney_hpack: ~.2f us/op~n~n", [Time / N]). - -bench_dynamic_table_single(N) -> - %% Generate headers to fill dynamic table - Headers = [{<<"x-custom-", (integer_to_binary(I))/binary>>, - <<"value-", (integer_to_binary(I))/binary>>} - || I <- lists:seq(1, 50)], - - State = lists:foldl(fun(H, S) -> - {_, S2} = hackney_hpack:encode([H], S), - S2 - end, hackney_hpack:init(), Headers), - - %% Benchmark lookups with different positions - bench_dyn_lookup(N, "Newest entry (position 1)", - [{<<"x-custom-50">>, <<"value-50">>}], State), - bench_dyn_lookup(N, "Middle entry (position 25)", - [{<<"x-custom-25">>, <<"value-25">>}], State), - bench_dyn_lookup(N, "Oldest entry (position 50)", - [{<<"x-custom-1">>, <<"value-1">>}], State). - -bench_dyn_lookup(N, Label, Headers, State) -> - {Time, _} = timer:tc(fun() -> - lists:foreach(fun(_) -> - hackney_hpack:encode(Headers, State) - end, lists:seq(1, N)) - end), - - io:format("~s:~n", [Label]), - io:format(" hackney_hpack O(1): ~.2f us/op~n~n", [Time / N]). - -bench_iteration(encode, Headers, Module, State) -> - {_, S2} = Module:encode(Headers, State), - S2. diff --git a/test/hackney_hpack_tests.erl b/test/hackney_hpack_tests.erl deleted file mode 100644 index 0ec7b6aa..00000000 --- a/test/hackney_hpack_tests.erl +++ /dev/null @@ -1,105 +0,0 @@ --module(hackney_hpack_tests). --include_lib("eunit/include/eunit.hrl"). - --define(STATIC_TABLE_SIZE, 61). - -encode_decode_roundtrip_test_() -> - {"HPACK encode/decode round-trip", [ - {"single encode/decode cycle preserves headers", - fun encode_decode_single/0}, - {"second encode uses dynamic table and still decodes correctly", - fun encode_decode_reuses_dynamic_table/0}, - {"multiple sequential requests round-trip correctly", - fun encode_decode_multiple_sequential/0}, - {"dynamic table indices start at STATIC_TABLE_SIZE + 1", - fun dynamic_table_index_starts_at_62/0} - ]}. - -encode_decode_single() -> - Headers = [ - {<<":method">>, <<"GET">>}, - {<<":path">>, <<"/">>}, - {<<":scheme">>, <<"https">>}, - {<<":authority">>, <<"example.com">>} - ], - {Encoded, EncState} = hackney_hpack:encode(Headers), - EncodedBin = iolist_to_binary(Encoded), - {Decoded, _DecState} = hackney_hpack:decode(EncodedBin), - ?assertEqual(Headers, Decoded), - %% Verify encoder state is usable for next request - ?assertNotEqual(hackney_hpack:init(), EncState). - -encode_decode_reuses_dynamic_table() -> - Headers = [ - {<<":method">>, <<"GET">>}, - {<<":path">>, <<"/api/test">>}, - {<<":scheme">>, <<"https">>}, - {<<":authority">>, <<"example.com">>}, - {<<"accept">>, <<"application/json">>} - ], - %% First encode populates the dynamic table - {Encoded1, EncState1} = hackney_hpack:encode(Headers), - Encoded1Bin = iolist_to_binary(Encoded1), - {Decoded1, DecState1} = hackney_hpack:decode(Encoded1Bin), - ?assertEqual(Headers, Decoded1), - - %% Second encode should use indexed references from dynamic table - {Encoded2, _EncState2} = hackney_hpack:encode(Headers, EncState1), - Encoded2Bin = iolist_to_binary(Encoded2), - - %% Second encoding should be smaller (using indexed references) - ?assert(byte_size(Encoded2Bin) < byte_size(Encoded1Bin)), - - %% Must still decode to the same headers - {Decoded2, _DecState2} = hackney_hpack:decode(Encoded2Bin, DecState1), - ?assertEqual(Headers, Decoded2). - -encode_decode_multiple_sequential() -> - EncState0 = hackney_hpack:init(), - DecState0 = hackney_hpack:init(), - Requests = [ - [{<<":method">>, <<"GET">>}, - {<<":path">>, <<"/">>}, - {<<":scheme">>, <<"https">>}, - {<<":authority">>, <<"example.com">>}], - [{<<":method">>, <<"GET">>}, - {<<":path">>, <<"/page2">>}, - {<<":scheme">>, <<"https">>}, - {<<":authority">>, <<"example.com">>}], - [{<<":method">>, <<"POST">>}, - {<<":path">>, <<"/api/data">>}, - {<<":scheme">>, <<"https">>}, - {<<":authority">>, <<"example.com">>}, - {<<"content-type">>, <<"application/json">>}], - [{<<":method">>, <<"GET">>}, - {<<":path">>, <<"/">>}, - {<<":scheme">>, <<"https">>}, - {<<":authority">>, <<"example.com">>}] - ], - lists:foldl(fun(Headers, {ES, DS}) -> - {Encoded, ES2} = hackney_hpack:encode(Headers, ES), - EncodedBin = iolist_to_binary(Encoded), - {Decoded, DS2} = hackney_hpack:decode(EncodedBin, DS), - ?assertEqual(Headers, Decoded), - {ES2, DS2} - end, {EncState0, DecState0}, Requests). - -dynamic_table_index_starts_at_62() -> - Headers = [{<<"x-custom">>, <<"value">>}], - {Encoded1, EncState1} = hackney_hpack:encode(Headers), - Encoded1Bin = iolist_to_binary(Encoded1), - {_, DecState1} = hackney_hpack:decode(Encoded1Bin), - - %% Second encode should produce an indexed header field representation - %% The index must be >= 62 (STATIC_TABLE_SIZE + 1) - {Encoded2, _} = hackney_hpack:encode(Headers, EncState1), - Encoded2Bin = iolist_to_binary(Encoded2), - - %% Indexed header field starts with 1-bit prefix: 1xxxxxxx - %% Extract the index from the 7-bit prefix integer - <<1:1, IndexBits:7, _/binary>> = Encoded2Bin, - ?assert(IndexBits >= ?STATIC_TABLE_SIZE + 1), - - %% Verify it decodes correctly - {Decoded2, _} = hackney_hpack:decode(Encoded2Bin, DecState1), - ?assertEqual(Headers, Decoded2). diff --git a/test/hackney_http2_e2e_SUITE.erl b/test/hackney_http2_e2e_SUITE.erl deleted file mode 100644 index a6980b9d..00000000 --- a/test/hackney_http2_e2e_SUITE.erl +++ /dev/null @@ -1,181 +0,0 @@ -%%% -*- erlang -*- -%%% -%%% This file is part of hackney released under the Apache 2 license. -%%% See the NOTICE for more information. -%%% -%%% Copyright (c) 2024-2026 Benoit Chesneau -%%% -%%% @doc End-to-end tests for HTTP/2 against real servers. -%%% -%%% These tests validate HTTP/2 compliance against production servers: -%%% - nghttp2.org: Reference HTTP/2 implementation -%%% - www.google.com: Strict HTTP/2 enforcement -%%% - cloudflare.com: CDN with HTTP/2 optimizations -%%% -%%% Tests are skipped if network is unavailable. -%%% -%%% To run: rebar3 ct --suite=hackney_http2_e2e_SUITE - --module(hackney_http2_e2e_SUITE). - --include_lib("common_test/include/ct.hrl"). - --export([ - all/0, - groups/0, - init_per_suite/1, - end_per_suite/1, - init_per_testcase/2, - end_per_testcase/2 -]). - --export([ - nghttp2_simple_get/1, - nghttp2_concurrent_streams/1, - google_strict_headers/1, - cloudflare_http2/1 -]). - --define(TIMEOUT, 30000). - -%%==================================================================== -%% CT Callbacks -%%==================================================================== - -all() -> - [{group, e2e_tests}]. - -groups() -> - [{e2e_tests, [sequence], [ - nghttp2_simple_get, - nghttp2_concurrent_streams, - google_strict_headers, - cloudflare_http2 - ]}]. - -init_per_suite(Config) -> - application:ensure_all_started(hackney), - Config. - -end_per_suite(_Config) -> - ok. - -init_per_testcase(_TestCase, Config) -> - %% Check network availability by doing a quick test - case check_network() of - ok -> Config; - {error, Reason} -> {skip, {network_unavailable, Reason}} - end. - -end_per_testcase(_TestCase, _Config) -> - ok. - -%%==================================================================== -%% Test Cases -%%==================================================================== - -%% @doc Simple GET request to nghttp2.org over HTTP/2. -nghttp2_simple_get(_Config) -> - URL = <<"https://nghttp2.org/">>, - Opts = [{protocols, [http2]}, {recv_timeout, ?TIMEOUT}], - - case hackney:get(URL, [], <<>>, Opts) of - {ok, Status, Headers, Body} -> - ct:log("Status: ~p", [Status]), - ct:log("Headers: ~p", [Headers]), - ct:log("Body length: ~p bytes", [byte_size(Body)]), - - %% Verify success - true = Status >= 200 andalso Status < 400, - - %% Verify HTTP/2 was used (nghttp2 server header) - case proplists:get_value(<<"server">>, Headers) of - undefined -> ok; - Server -> ct:log("Server: ~s", [Server]) - end, - ok; - {error, Reason} -> - ct:fail({request_failed, Reason}) - end. - -%% @doc Test concurrent streams to nghttp2.org. -nghttp2_concurrent_streams(_Config) -> - URL = <<"https://nghttp2.org/">>, - Opts = [{protocols, [http2]}, {recv_timeout, ?TIMEOUT}], - - %% Launch 5 concurrent requests - Self = self(), - Pids = [spawn_link(fun() -> - Result = hackney:get(URL, [], <<>>, Opts), - Self ! {done, self(), Result} - end) || _ <- lists:seq(1, 5)], - - %% Collect results - Results = [receive - {done, Pid, Result} -> Result - after ?TIMEOUT * 2 -> - {error, timeout} - end || Pid <- Pids], - - %% All should succeed - lists:foreach(fun - ({ok, Status, _Headers, _Body}) when Status >= 200, Status < 400 -> - ok; - ({ok, Status, _Headers, _}) -> - ct:fail({unexpected_status, Status}); - ({error, Reason}) -> - ct:fail({request_failed, Reason}) - end, Results), - ok. - -%% @doc Test against Google which has strict HTTP/2 header validation. -%% This test caught the Host header bug in PR #811. -google_strict_headers(_Config) -> - URL = <<"https://www.google.com/">>, - Opts = [{protocols, [http2]}, {recv_timeout, ?TIMEOUT}], - - case hackney:get(URL, [], <<>>, Opts) of - {ok, Status, Headers, _Body} -> - ct:log("Google Status: ~p", [Status]), - ct:log("Google Headers: ~p", [Headers]), - - %% Google should return a valid response - true = Status >= 200 andalso Status < 500, - ok; - {error, Reason} -> - ct:fail({google_request_failed, Reason}) - end. - -%% @doc Test against Cloudflare's HTTP/2 implementation. -cloudflare_http2(_Config) -> - URL = <<"https://cloudflare.com/">>, - Opts = [{protocols, [http2]}, {recv_timeout, ?TIMEOUT}], - - case hackney:get(URL, [], <<>>, Opts) of - {ok, Status, Headers, _Body} -> - ct:log("Cloudflare Status: ~p", [Status]), - - %% Check for Cloudflare server header - case proplists:get_value(<<"server">>, Headers) of - undefined -> ok; - Server -> ct:log("Server: ~s", [Server]) - end, - - %% Should get a valid response - true = Status >= 200 andalso Status < 500, - ok; - {error, Reason} -> - ct:fail({cloudflare_request_failed, Reason}) - end. - -%%==================================================================== -%% Internal Functions -%%==================================================================== - -%% @private Check if network is available. -check_network() -> - %% Try to resolve a well-known hostname - case inet:gethostbyname("nghttp2.org") of - {ok, _} -> ok; - {error, Reason} -> {error, Reason} - end. diff --git a/test/hackney_http2_machine_bench.erl b/test/hackney_http2_machine_bench.erl deleted file mode 100644 index 00091bcd..00000000 --- a/test/hackney_http2_machine_bench.erl +++ /dev/null @@ -1,216 +0,0 @@ -%%% -*- erlang -*- -%%% -%%% This file is part of hackney released under the Apache 2 license. -%%% See the NOTICE for more information. -%%% -%%% Copyright (c) 2024-2026 Benoit Chesneau -%%% -%%% @doc Benchmarks for HTTP/2 state machine optimizations. -%%% -%%% Measures performance of: -%%% - Stream operations (init, get, store) -%%% - Header preparation -%%% - Flow control window updates -%%% - Lingering stream lookups (gb_sets vs lists) -%%% -%%% Run: rebar3 as test compile && erl -pa _build/test/lib/*/ebin \ -%%% -noshell -eval 'hackney_http2_machine_bench:run().' -s init stop - --module(hackney_http2_machine_bench). - --export([run/0, run/1]). --export([bench_stream_init/0, bench_stream_lookup/0, bench_headers/0]). --export([bench_window_update/0, bench_lingering_streams/0]). - --define(DEFAULT_ITERATIONS, 10000). --define(TEST_OPTS, #{preface_timeout => infinity, settings_timeout => infinity}). - -%%==================================================================== -%% Public API -%%==================================================================== - -run() -> - run(?DEFAULT_ITERATIONS). - -run(N) -> - io:format("~n=== HTTP/2 Machine Benchmark Suite ===~n"), - io:format("Iterations: ~p~n~n", [N]), - - %% Warm up JIT - io:format("Warming up...~n"), - {ok, _, S0} = hackney_http2_machine:init(client, ?TEST_OPTS), - _ = [begin - {ok, _, S1} = hackney_http2_machine:init_stream(<<"GET">>, S0), - S1 - end || _ <- lists:seq(1, 1000)], - - io:format("~n--- Stream Operations ---~n~n"), - bench_stream_init_n(N), - bench_stream_lookup_n(N), - - io:format("~n--- Header Preparation ---~n~n"), - bench_headers_n(N), - - io:format("~n--- Flow Control ---~n~n"), - bench_window_update_n(N), - - io:format("~n--- Lingering Streams (gb_sets) ---~n~n"), - bench_lingering_streams_n(N), - - io:format("~n=== Benchmark Complete ===~n"), - ok. - -%%==================================================================== -%% Individual Benchmarks -%%==================================================================== - -bench_stream_init() -> - bench_stream_init_n(?DEFAULT_ITERATIONS). - -bench_stream_init_n(N) -> - {ok, _, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - - {Time, _} = timer:tc(fun() -> - lists:foldl(fun(_, S) -> - {ok, _, S1} = hackney_http2_machine:init_stream(<<"GET">>, S), - S1 - end, State0, lists:seq(1, N)) - end), - - io:format("Stream init:~n"), - io:format(" ~.2f us/op (~p ops in ~.2f ms)~n~n", - [Time / N, N, Time / 1000]). - -bench_stream_lookup() -> - bench_stream_lookup_n(?DEFAULT_ITERATIONS). - -bench_stream_lookup_n(N) -> - %% Create state with multiple streams - {ok, _, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - State1 = lists:foldl(fun(_, S) -> - {ok, _, S1} = hackney_http2_machine:init_stream(<<"GET">>, S), - S1 - end, State0, lists:seq(1, 100)), - - %% Benchmark lookups on stream 51 (middle) - {Time, _} = timer:tc(fun() -> - lists:foreach(fun(_) -> - hackney_http2_machine:get_stream_local_state(51, State1) - end, lists:seq(1, N)) - end), - - io:format("Stream lookup (100 streams, middle):~n"), - io:format(" ~.2f us/op (~p ops in ~.2f ms)~n~n", - [Time / N, N, Time / 1000]). - -bench_headers() -> - bench_headers_n(?DEFAULT_ITERATIONS). - -bench_headers_n(N) -> - {ok, _, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - {ok, StreamId, State1} = hackney_http2_machine:init_stream(<<"GET">>, State0), - - PseudoHeaders = #{ - method => <<"GET">>, - scheme => <<"https">>, - authority => <<"example.com">>, - path => <<"/">> - }, - Headers = [ - {<<"user-agent">>, <<"hackney/benchmark">>}, - {<<"accept">>, <<"*/*">>} - ], - - {Time, _} = timer:tc(fun() -> - lists:foreach(fun(_) -> - hackney_http2_machine:prepare_headers( - StreamId, State1, fin, PseudoHeaders, Headers) - end, lists:seq(1, N)) - end), - - io:format("Header preparation:~n"), - io:format(" ~.2f us/op (~p ops in ~.2f ms)~n~n", - [Time / N, N, Time / 1000]). - -bench_window_update() -> - bench_window_update_n(?DEFAULT_ITERATIONS). - -bench_window_update_n(N) -> - {ok, _, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - {ok, StreamId, State1} = hackney_http2_machine:init_stream(<<"GET">>, State0), - - {Time, _} = timer:tc(fun() -> - lists:foreach(fun(_) -> - hackney_http2_machine:update_window(StreamId, 1024, State1) - end, lists:seq(1, N)) - end), - - io:format("Window update (stream):~n"), - io:format(" ~.2f us/op (~p ops in ~.2f ms)~n~n", - [Time / N, N, Time / 1000]). - -bench_lingering_streams() -> - bench_lingering_streams_n(?DEFAULT_ITERATIONS). - -bench_lingering_streams_n(N) -> - {ok, _, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - - %% Create 50 streams and reset them to build up lingering list - State1 = lists:foldl(fun(_, S) -> - {ok, StreamId, S1} = hackney_http2_machine:init_stream(<<"GET">>, S), - {ok, S2} = hackney_http2_machine:reset_stream(StreamId, S1), - S2 - end, State0, lists:seq(1, 50)), - - %% Benchmark is_lingering_stream lookups - {Time, _} = timer:tc(fun() -> - lists:foreach(fun(_) -> - %% Check a stream that is lingering - hackney_http2_machine:is_lingering_stream(25, State1), - %% Check a stream that is not lingering - hackney_http2_machine:is_lingering_stream(999, State1) - end, lists:seq(1, N)) - end), - - io:format("Lingering stream lookup (50 streams, gb_sets):~n"), - io:format(" ~.2f us/op (~p ops in ~.2f ms)~n~n", - [Time / (N * 2), N * 2, Time / 1000]). - -%%==================================================================== -%% Comparison Utilities -%%==================================================================== - -%% @doc Compare gb_sets vs list performance for lingering streams. -%% This demonstrates the O(log N) vs O(N) difference. -compare_lingering_implementation() -> - io:format("~n=== Lingering Stream Implementation Comparison ===~n~n"), - - Sizes = [10, 50, 100], - N = 100000, - - lists:foreach(fun(Size) -> - %% Build list - List = lists:seq(1, Size * 2, 2), %% Odd numbers - - %% Build gb_set - Set = gb_sets:from_list(List), - - %% Benchmark list lookup - {TimeList, _} = timer:tc(fun() -> - lists:foreach(fun(_) -> - lists:member(Size, List) - end, lists:seq(1, N)) - end), - - %% Benchmark gb_sets lookup - {TimeSet, _} = timer:tc(fun() -> - lists:foreach(fun(_) -> - gb_sets:is_member(Size, Set) - end, lists:seq(1, N)) - end), - - io:format("Size ~p:~n", [Size]), - io:format(" lists:member ~.3f us/op~n", [TimeList / N]), - io:format(" gb_sets:is_member ~.3f us/op~n", [TimeSet / N]), - io:format(" Speedup: ~.2fx~n~n", [TimeList / max(TimeSet, 1)]) - end, Sizes). diff --git a/test/hackney_http2_machine_tests.erl b/test/hackney_http2_machine_tests.erl deleted file mode 100644 index 150fe1c1..00000000 --- a/test/hackney_http2_machine_tests.erl +++ /dev/null @@ -1,361 +0,0 @@ -%%% -*- erlang -*- -%%% -%%% This file is part of hackney released under the Apache 2 license. -%%% See the NOTICE for more information. -%%% -%%% Copyright (c) 2024-2026 Benoit Chesneau -%%% -%%% @doc Tests for HTTP/2 state machine module. -%%% - --module(hackney_http2_machine_tests). - --include_lib("eunit/include/eunit.hrl"). - -%% Default options that disable timers to prevent orphaned timer messages during tests --define(TEST_OPTS, #{preface_timeout => infinity, settings_timeout => infinity}). - -%%==================================================================== -%% Client Mode Tests -%%==================================================================== - -%% Test client initialization -init_client_test() -> - {ok, Preface, State} = hackney_http2_machine:init(client, ?TEST_OPTS), - ?assert(is_binary(iolist_to_binary(Preface))), - %% Client preface should include "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" - PrefaceBin = iolist_to_binary(Preface), - ?assertMatch(<<"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", _/binary>>, PrefaceBin), - ?assert(is_tuple(State)). - -%% Test server initialization -init_server_test() -> - {ok, Preface, State} = hackney_http2_machine:init(server, ?TEST_OPTS), - ?assert(is_binary(iolist_to_binary(Preface))), - ?assert(is_tuple(State)). - -%% Test client can initialize a stream -init_stream_client_test() -> - {ok, _Preface, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - %% Client needs to acknowledge server settings first - %% Let's just test we can call init_stream - {ok, StreamID, State1} = hackney_http2_machine:init_stream(<<"GET">>, State0), - ?assertEqual(1, StreamID), - %% Next stream should be 3 - {ok, StreamID2, _State2} = hackney_http2_machine:init_stream(<<"POST">>, State1), - ?assertEqual(3, StreamID2). - -%% Test stream IDs increment correctly -stream_id_increment_test() -> - {ok, _Preface, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - {ok, 1, State1} = hackney_http2_machine:init_stream(<<"GET">>, State0), - {ok, 3, State2} = hackney_http2_machine:init_stream(<<"GET">>, State1), - {ok, 5, State3} = hackney_http2_machine:init_stream(<<"GET">>, State2), - {ok, 7, _State4} = hackney_http2_machine:init_stream(<<"GET">>, State3). - -%%==================================================================== -%% Settings Tests -%%==================================================================== - -%% Test custom initial settings -init_with_custom_settings_test() -> - Opts = maps:merge(?TEST_OPTS, #{ - initial_stream_window_size => 32768, - max_concurrent_streams => 50 - }), - {ok, Preface, _State} = hackney_http2_machine:init(client, Opts), - PrefaceBin = iolist_to_binary(Preface), - %% Should contain the connection preface - ?assertMatch(<<"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", _/binary>>, PrefaceBin). - -%% Test get local settings -get_local_setting_test() -> - {ok, _Preface, State} = hackney_http2_machine:init(client, ?TEST_OPTS), - %% Default initial window size is 65535 - ?assertEqual(65535, hackney_http2_machine:get_local_setting(initial_window_size, State)). - -%% Test get remote settings (defaults) -get_remote_settings_test() -> - {ok, _Preface, State} = hackney_http2_machine:init(client, ?TEST_OPTS), - RemoteSettings = hackney_http2_machine:get_remote_settings(State), - ?assert(is_map(RemoteSettings)), - ?assertEqual(65535, maps:get(initial_window_size, RemoteSettings)). - -%%==================================================================== -%% Headers Preparation Tests -%%==================================================================== - -%% Test prepare_headers for a GET request -prepare_headers_get_test() -> - {ok, _Preface, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - {ok, StreamID, State1} = hackney_http2_machine:init_stream(<<"GET">>, State0), - PseudoHeaders = #{ - method => <<"GET">>, - scheme => <<"https">>, - authority => <<"example.com">>, - path => <<"/">> - }, - Headers = [{<<"user-agent">>, <<"hackney">>}], - {ok, IsFin, HeaderBlock, _State2} = hackney_http2_machine:prepare_headers( - StreamID, State1, fin, PseudoHeaders, Headers), - ?assertEqual(fin, IsFin), - ?assert(is_binary(iolist_to_binary(HeaderBlock))). - -%% Test prepare_headers for a POST request -prepare_headers_post_test() -> - {ok, _Preface, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - {ok, StreamID, State1} = hackney_http2_machine:init_stream(<<"POST">>, State0), - PseudoHeaders = #{ - method => <<"POST">>, - scheme => <<"https">>, - authority => <<"example.com">>, - path => <<"/api/data">> - }, - Headers = [ - {<<"content-type">>, <<"application/json">>}, - {<<"content-length">>, <<"100">>} - ], - {ok, IsFin, HeaderBlock, _State2} = hackney_http2_machine:prepare_headers( - StreamID, State1, nofin, PseudoHeaders, Headers), - ?assertEqual(nofin, IsFin), - ?assert(is_binary(iolist_to_binary(HeaderBlock))). - -%%==================================================================== -%% Frame Handling Tests -%%==================================================================== - -%% Test SETTINGS frame handling -settings_frame_test() -> - {ok, _Preface, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - %% Simulate receiving a SETTINGS frame from server - SettingsFrame = {settings, #{initial_window_size => 32768}}, - {ok, State1} = hackney_http2_machine:frame(SettingsFrame, State0), - ?assert(is_tuple(State1)). - -%% Test SETTINGS ACK frame -settings_ack_frame_test() -> - {ok, _Preface, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - %% First receive settings from server - SettingsFrame = {settings, #{}}, - {ok, State1} = hackney_http2_machine:frame(SettingsFrame, State0), - %% Then receive ACK for our settings - {ok, State2} = hackney_http2_machine:frame(settings_ack, State1), - ?assert(is_tuple(State2)). - -%% Test PING frame -ping_frame_test() -> - {ok, _Preface, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - %% First need to get past settings state - {ok, State1} = hackney_http2_machine:frame({settings, #{}}, State0), - %% Now handle PING - PingFrame = {ping, 12345}, - {ok, State2} = hackney_http2_machine:frame(PingFrame, State1), - ?assert(is_tuple(State2)). - -%% Test PING ACK frame -ping_ack_frame_test() -> - {ok, _Preface, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - {ok, State1} = hackney_http2_machine:frame({settings, #{}}, State0), - PingAckFrame = {ping_ack, 12345}, - {ok, State2} = hackney_http2_machine:frame(PingAckFrame, State1), - ?assert(is_tuple(State2)). - -%% Test GOAWAY frame -goaway_frame_test() -> - {ok, _Preface, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - {ok, State1} = hackney_http2_machine:frame({settings, #{}}, State0), - GoawayFrame = {goaway, 0, no_error, <<>>}, - {ok, {goaway, 0, no_error, <<>>}, _State2} = hackney_http2_machine:frame(GoawayFrame, State1). - -%%==================================================================== -%% Window Management Tests -%%==================================================================== - -%% Test update_window for connection -update_window_connection_test() -> - {ok, _Preface, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - State1 = hackney_http2_machine:update_window(1000, State0), - ?assert(is_tuple(State1)). - -%% Test get connection local buffer size -get_connection_buffer_size_test() -> - {ok, _Preface, State} = hackney_http2_machine:init(client, ?TEST_OPTS), - BufferSize = hackney_http2_machine:get_connection_local_buffer_size(State), - ?assertEqual(0, BufferSize). - -%%==================================================================== -%% Stream State Tests -%%==================================================================== - -%% Test get_stream_local_state -get_stream_local_state_test() -> - {ok, _Preface, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - {ok, StreamID, State1} = hackney_http2_machine:init_stream(<<"GET">>, State0), - {ok, LocalState, BufferState} = hackney_http2_machine:get_stream_local_state(StreamID, State1), - ?assertEqual(idle, LocalState), - ?assertEqual(empty, BufferState). - -%% Test get_stream_remote_state -get_stream_remote_state_test() -> - {ok, _Preface, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - {ok, StreamID, State1} = hackney_http2_machine:init_stream(<<"GET">>, State0), - {ok, RemoteState} = hackney_http2_machine:get_stream_remote_state(StreamID, State1), - ?assertEqual(idle, RemoteState). - -%% Test stream not found -get_stream_state_not_found_test() -> - {ok, _Preface, State} = hackney_http2_machine:init(client, ?TEST_OPTS), - ?assertEqual({error, not_found}, hackney_http2_machine:get_stream_local_state(999, State)), - ?assertEqual({error, not_found}, hackney_http2_machine:get_stream_remote_state(999, State)). - -%%==================================================================== -%% Last StreamID Tests -%%==================================================================== - -%% Test get/set last streamid -last_streamid_test() -> - {ok, _Preface, State0} = hackney_http2_machine:init(client, ?TEST_OPTS), - %% For a client, last_remote_streamid starts at 0 (no server-initiated streams yet) - LastStreamID = hackney_http2_machine:get_last_streamid(State0), - ?assertEqual(0, LastStreamID), - %% Set last streamid (returns current remote streamid) - {RemoteStreamID, State1} = hackney_http2_machine:set_last_streamid(State0), - ?assertEqual(0, RemoteStreamID), - ?assert(is_tuple(State1)). - -%%==================================================================== -%% Lingering Stream Tests -%%==================================================================== - -%% Test is_lingering_stream -is_lingering_stream_test() -> - {ok, _Preface, State} = hackney_http2_machine:init(client, ?TEST_OPTS), - ?assertEqual(false, hackney_http2_machine:is_lingering_stream(1, State)). - -%%==================================================================== -%% Window Update Send Tests -%%==================================================================== - -setup_stream_with_headers(ServerSettings) -> - {ok, _Preface, S0} = hackney_http2_machine:init(client, ?TEST_OPTS), - {ok, S1} = hackney_http2_machine:frame({settings, ServerSettings}, S0), - {ok, S2} = hackney_http2_machine:frame(settings_ack, S1), - {ok, StreamID, S3} = hackney_http2_machine:init_stream(<<"POST">>, S2), - PseudoHeaders = #{ - method => <<"POST">>, - scheme => <<"https">>, - authority => <<"example.com">>, - path => <<"/upload">> - }, - Headers = [{<<"content-type">>, <<"application/octet-stream">>}], - {ok, nofin, _HeaderBlock, S4} = hackney_http2_machine:prepare_headers( - StreamID, S3, nofin, PseudoHeaders, Headers), - {StreamID, S4}. - -conn_window_update_sends_queued_data_test() -> - {StreamID, S0} = setup_stream_with_headers(#{initial_window_size => 200000}), - FillData = {data, binary:copy(<<0>>, 65535)}, - {send, _, S1} = hackney_http2_machine:send_or_queue_data(StreamID, S0, nofin, FillData), - QueuedData = {data, binary:copy(<<1>>, 10000)}, - {ok, S2} = hackney_http2_machine:send_or_queue_data(StreamID, S1, fin, QueuedData), - BufferSize = hackney_http2_machine:get_connection_local_buffer_size(S2), - ?assert(BufferSize > 0), - {send, SendList, _S3} = hackney_http2_machine:frame({window_update, 50000}, S2), - ?assert(is_list(SendList)), - ?assert(length(SendList) > 0). - -stream_window_update_sends_queued_data_test() -> - {StreamID, S0} = setup_stream_with_headers(#{initial_window_size => 100}), - FillData = {data, binary:copy(<<0>>, 100)}, - {send, _, S1} = hackney_http2_machine:send_or_queue_data(StreamID, S0, nofin, FillData), - QueuedData = {data, binary:copy(<<1>>, 500)}, - {ok, S2} = hackney_http2_machine:send_or_queue_data(StreamID, S1, fin, QueuedData), - {send, SendList, _S3} = hackney_http2_machine:frame( - {window_update, StreamID, 10000}, S2), - ?assert(is_list(SendList)), - ?assert(length(SendList) > 0), - [{SentStreamID, _, _} | _] = SendList, - ?assertEqual(StreamID, SentStreamID). - -conn_window_update_no_queued_data_test() -> - {ok, _Preface, S0} = hackney_http2_machine:init(client, ?TEST_OPTS), - {ok, S1} = hackney_http2_machine:frame({settings, #{}}, S0), - {ok, S2} = hackney_http2_machine:frame(settings_ack, S1), - {ok, S3} = hackney_http2_machine:frame({window_update, 1000}, S2), - ?assert(is_tuple(S3)). - -stream_window_update_no_queued_data_test() -> - {StreamID, S0} = setup_stream_with_headers(#{}), - {ok, S1} = hackney_http2_machine:frame({window_update, StreamID, 1000}, S0), - ?assert(is_tuple(S1)). - -conn_window_update_overflow_test() -> - {ok, _Preface, S0} = hackney_http2_machine:init(client, ?TEST_OPTS), - {ok, S1} = hackney_http2_machine:frame({settings, #{}}, S0), - {ok, S2} = hackney_http2_machine:frame(settings_ack, S1), - {error, {connection_error, flow_control_error, _}, _S3} = - hackney_http2_machine:frame({window_update, 16#7fffffff}, S2). - -stream_window_update_overflow_test() -> - {StreamID, S0} = setup_stream_with_headers(#{}), - Result = hackney_http2_machine:frame({window_update, StreamID, 16#7fffffff}, S0), - ?assertMatch({error, {stream_error, StreamID, flow_control_error, _}, _}, Result). - -%%==================================================================== -%% Test Suites -%%==================================================================== - -client_init_test_() -> - [ - {"Client init", fun init_client_test/0}, - {"Server init", fun init_server_test/0}, - {"Init stream", fun init_stream_client_test/0}, - {"Stream ID increment", fun stream_id_increment_test/0} - ]. - -settings_test_() -> - [ - {"Custom settings", fun init_with_custom_settings_test/0}, - {"Get local setting", fun get_local_setting_test/0}, - {"Get remote settings", fun get_remote_settings_test/0} - ]. - -headers_test_() -> - [ - {"Prepare GET headers", fun prepare_headers_get_test/0}, - {"Prepare POST headers", fun prepare_headers_post_test/0} - ]. - -frame_handling_test_() -> - [ - {"SETTINGS frame", fun settings_frame_test/0}, - {"SETTINGS ACK", fun settings_ack_frame_test/0}, - {"PING frame", fun ping_frame_test/0}, - {"PING ACK frame", fun ping_ack_frame_test/0}, - {"GOAWAY frame", fun goaway_frame_test/0} - ]. - -stream_state_test_() -> - [ - {"Get stream local state", fun get_stream_local_state_test/0}, - {"Get stream remote state", fun get_stream_remote_state_test/0}, - {"Stream not found", fun get_stream_state_not_found_test/0}, - {"Last streamid", fun last_streamid_test/0}, - {"Is lingering stream", fun is_lingering_stream_test/0} - ]. - -window_update_send_test_() -> - [ - {"Conn window_update sends queued data", - fun conn_window_update_sends_queued_data_test/0}, - {"Stream window_update sends queued data", - fun stream_window_update_sends_queued_data_test/0}, - {"Conn window_update with no queued data", - fun conn_window_update_no_queued_data_test/0}, - {"Stream window_update with no queued data", - fun stream_window_update_no_queued_data_test/0}, - {"Conn window_update overflow error", - fun conn_window_update_overflow_test/0}, - {"Stream window_update overflow error", - fun stream_window_update_overflow_test/0} - ]. diff --git a/test/hackney_http2_tests.erl b/test/hackney_http2_tests.erl deleted file mode 100644 index 85ab4782..00000000 --- a/test/hackney_http2_tests.erl +++ /dev/null @@ -1,337 +0,0 @@ -%%% -*- erlang -*- -%%% -%%% This file is part of hackney released under the Apache 2 license. -%%% See the NOTICE for more information. -%%% -%%% Copyright (c) 2024-2026 Benoit Chesneau -%%% -%%% @doc Tests for HTTP/2 frame parsing/building and HPACK encoding/decoding. -%%% - --module(hackney_http2_tests). - --include_lib("eunit/include/eunit.hrl"). - -%%==================================================================== -%% HTTP/2 Frame Parsing Tests -%%==================================================================== - -%% Test connection preface parsing -parse_connection_preface_test() -> - Preface = <<"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n">>, - ?assertEqual({ok, <<>>}, hackney_http2:parse_sequence(Preface)), - ?assertEqual({ok, <<"extra">>}, hackney_http2:parse_sequence(<>)), - ?assertEqual(more, hackney_http2:parse_sequence(<<"PRI * HTTP/2.0\r\n">>)), - ?assertMatch({connection_error, protocol_error, _}, - hackney_http2:parse_sequence(<<"INVALID PREFACE HERE!!!">>)). - -%% Test DATA frame parsing -parse_data_frame_test() -> - %% Build and parse a DATA frame - StreamID = 1, - Data = <<"Hello, HTTP/2!">>, - Frame = hackney_http2:data(StreamID, nofin, Data), - FrameBin = iolist_to_binary(Frame), - ?assertMatch({ok, {data, StreamID, nofin, Data}, <<>>}, - hackney_http2:parse(FrameBin)). - -parse_data_frame_with_fin_test() -> - StreamID = 3, - Data = <<"Goodbye">>, - Frame = hackney_http2:data(StreamID, fin, Data), - FrameBin = iolist_to_binary(Frame), - ?assertMatch({ok, {data, StreamID, fin, Data}, <<>>}, - hackney_http2:parse(FrameBin)). - -%% Test HEADERS frame parsing -parse_headers_frame_test() -> - StreamID = 1, - HeaderBlock = <<1, 2, 3, 4, 5>>, - Frame = hackney_http2:headers(StreamID, nofin, HeaderBlock), - FrameBin = iolist_to_binary(Frame), - ?assertMatch({ok, {headers, StreamID, nofin, head_fin, HeaderBlock}, <<>>}, - hackney_http2:parse(FrameBin)). - -%% Test SETTINGS frame parsing -parse_settings_frame_test() -> - Settings = #{ - header_table_size => 4096, - enable_push => true, - max_concurrent_streams => 100, - initial_window_size => 65535, - max_frame_size => 16384 - }, - Frame = hackney_http2:settings(Settings), - FrameBin = iolist_to_binary(Frame), - {ok, {settings, ParsedSettings}, <<>>} = hackney_http2:parse(FrameBin), - ?assertEqual(4096, maps:get(header_table_size, ParsedSettings)), - ?assertEqual(true, maps:get(enable_push, ParsedSettings)), - ?assertEqual(100, maps:get(max_concurrent_streams, ParsedSettings)). - -%% Test SETTINGS ACK -parse_settings_ack_test() -> - Frame = hackney_http2:settings_ack(), - ?assertMatch({ok, settings_ack, <<>>}, hackney_http2:parse(Frame)). - -%% Test PING frame -parse_ping_frame_test() -> - Opaque = 1234567890, - Frame = hackney_http2:ping(Opaque), - ?assertMatch({ok, {ping, Opaque}, <<>>}, hackney_http2:parse(Frame)). - -%% Test PING ACK -parse_ping_ack_test() -> - Opaque = 9876543210, - Frame = hackney_http2:ping_ack(Opaque), - ?assertMatch({ok, {ping_ack, Opaque}, <<>>}, hackney_http2:parse(Frame)). - -%% Test GOAWAY frame -parse_goaway_frame_test() -> - LastStreamID = 5, - ErrorCode = no_error, - DebugData = <<"goodbye">>, - Frame = hackney_http2:goaway(LastStreamID, ErrorCode, DebugData), - FrameBin = iolist_to_binary(Frame), - ?assertMatch({ok, {goaway, LastStreamID, no_error, DebugData}, <<>>}, - hackney_http2:parse(FrameBin)). - -%% Test WINDOW_UPDATE frame (connection level) -parse_window_update_connection_test() -> - Increment = 65535, - Frame = hackney_http2:window_update(Increment), - ?assertMatch({ok, {window_update, Increment}, <<>>}, - hackney_http2:parse(Frame)). - -%% Test WINDOW_UPDATE frame (stream level) -parse_window_update_stream_test() -> - StreamID = 1, - Increment = 32768, - Frame = hackney_http2:window_update(StreamID, Increment), - ?assertMatch({ok, {window_update, StreamID, Increment}, <<>>}, - hackney_http2:parse(Frame)). - -%% Test RST_STREAM frame -parse_rst_stream_test() -> - StreamID = 1, - Frame = hackney_http2:rst_stream(StreamID, cancel), - ?assertMatch({ok, {rst_stream, StreamID, cancel}, <<>>}, - hackney_http2:parse(Frame)). - -%% Test PRIORITY frame -parse_priority_test() -> - StreamID = 3, - DepStreamID = 1, - Weight = 16, - Frame = hackney_http2:priority(StreamID, shared, DepStreamID, Weight), - ?assertMatch({ok, {priority, StreamID, shared, DepStreamID, _}, <<>>}, - hackney_http2:parse(Frame)). - -%% Test incomplete frame returns more -parse_incomplete_frame_test() -> - %% Send only part of a frame - PartialFrame = <<0, 0, 10, 0, 0, 0, 0, 0, 1, "hel">>, - ?assertEqual(more, hackney_http2:parse(PartialFrame)). - -%% Test frame with trailing data -parse_frame_with_extra_data_test() -> - Opaque = 42, - Frame = hackney_http2:ping(Opaque), - FrameWithExtra = <>, - ?assertMatch({ok, {ping, Opaque}, <<"extra">>}, - hackney_http2:parse(FrameWithExtra)). - -%%==================================================================== -%% HTTP/2 Frame Building Tests -%%==================================================================== - -build_data_frame_test() -> - Frame = hackney_http2:data(1, nofin, <<"test">>), - FrameBin = iolist_to_binary(Frame), - %% Verify frame header: length=4, type=0 (DATA), flags=0, stream_id=1 - <<4:24, 0:8, 0:8, 0:1, 1:31, "test">> = FrameBin. - -build_headers_frame_test() -> - HeaderBlock = <<"headers">>, - Frame = hackney_http2:headers(1, fin, HeaderBlock), - FrameBin = iolist_to_binary(Frame), - %% Verify frame type is 1 (HEADERS), END_STREAM and END_HEADERS flags set - <<7:24, 1:8, _Flags:8, 0:1, 1:31, "headers">> = FrameBin. - -build_settings_frame_test() -> - Settings = #{initial_window_size => 65535}, - Frame = hackney_http2:settings(Settings), - FrameBin = iolist_to_binary(Frame), - %% Verify frame type is 4 (SETTINGS) - <<_:24, 4:8, _/binary>> = FrameBin. - -%%==================================================================== -%% HPACK Encoding/Decoding Tests -%%==================================================================== - -%% Test HPACK state initialization -hpack_init_test() -> - State = hackney_hpack:init(), - ?assert(is_tuple(State)). - -hpack_init_with_max_size_test() -> - State = hackney_hpack:init(8192), - ?assert(is_tuple(State)). - -%% Test HPACK encode/decode roundtrip -hpack_encode_decode_test() -> - Headers = [ - {<<":method">>, <<"GET">>}, - {<<":scheme">>, <<"https">>}, - {<<":path">>, <<"/">>}, - {<<":authority">>, <<"example.com">>} - ], - {Encoded, _State1} = hackney_hpack:encode(Headers), - EncodedBin = iolist_to_binary(Encoded), - {Decoded, _State2} = hackney_hpack:decode(EncodedBin), - ?assertEqual(Headers, Decoded). - -%% Test HPACK with custom headers -hpack_custom_headers_test() -> - Headers = [ - {<<":method">>, <<"POST">>}, - {<<":scheme">>, <<"https">>}, - {<<":path">>, <<"/api/v1/data">>}, - {<<":authority">>, <<"api.example.com">>}, - {<<"content-type">>, <<"application/json">>}, - {<<"x-custom-header">>, <<"custom-value">>} - ], - {Encoded, _State1} = hackney_hpack:encode(Headers), - EncodedBin = iolist_to_binary(Encoded), - {Decoded, _State2} = hackney_hpack:decode(EncodedBin), - ?assertEqual(Headers, Decoded). - -%% Test HPACK with static table entries -hpack_static_table_test() -> - %% These headers should use indexed representation from static table - Headers = [ - {<<":method">>, <<"GET">>}, - {<<":scheme">>, <<"http">>}, - {<<":path">>, <<"/">>} - ], - {Encoded, _State} = hackney_hpack:encode(Headers), - EncodedBin = iolist_to_binary(Encoded), - %% Static table entries should be very compact (1 byte each) - ?assert(byte_size(EncodedBin) < 10). - -%% Test HPACK encoding with huffman disabled -hpack_no_huffman_test() -> - Headers = [ - {<<":method">>, <<"GET">>}, - {<<":path">>, <<"/test/path">>} - ], - {EncodedHuffman, _} = hackney_hpack:encode(Headers), - {EncodedRaw, _} = hackney_hpack:encode(Headers, hackney_hpack:init(), #{huffman => false}), - %% Raw encoding should typically be larger than huffman - _HuffmanSize = iolist_size(EncodedHuffman), - _RawSize = iolist_size(EncodedRaw), - %% Both should decode to the same headers - {DecodedHuffman, _} = hackney_hpack:decode(iolist_to_binary(EncodedHuffman)), - {DecodedRaw, _} = hackney_hpack:decode(iolist_to_binary(EncodedRaw)), - ?assertEqual(DecodedHuffman, DecodedRaw). - -%% Test HPACK dynamic table - verify state can be reused for multiple encode/decode cycles -hpack_dynamic_table_test() -> - %% Test that multiple encode/decode operations work with the same state - %% Each encode/decode cycle uses fresh state since encoder/decoder states diverge - Headers1 = [ - {<<":method">>, <<"GET">>}, - {<<":authority">>, <<"www.example.com">>} - ], - - %% First roundtrip - {Encoded1, State1} = hackney_hpack:encode(Headers1), - {Decoded1, _} = hackney_hpack:decode(iolist_to_binary(Encoded1)), - ?assertEqual(Headers1, Decoded1), - - %% Second roundtrip with different headers to verify state handling - Headers2 = [ - {<<":method">>, <<"POST">>}, - {<<":path">>, <<"/api/users">>}, - {<<":authority">>, <<"api.example.com">>} - ], - {Encoded2, _State2} = hackney_hpack:encode(Headers2, State1), - {Decoded2, _} = hackney_hpack:decode(iolist_to_binary(Encoded2)), - ?assertEqual(Headers2, Decoded2), - - %% Verify second encoding is valid (can be decoded) - EncodedBin2 = iolist_to_binary(Encoded2), - ?assert(byte_size(EncodedBin2) > 0). - -%% Test HPACK max size update -hpack_set_max_size_test() -> - State0 = hackney_hpack:init(4096), - State1 = hackney_hpack:set_max_size(8192, State0), - ?assert(is_tuple(State1)). - -%% Test response headers encoding/decoding -hpack_response_headers_test() -> - Headers = [ - {<<":status">>, <<"200">>}, - {<<"content-type">>, <<"text/html">>}, - {<<"content-length">>, <<"1234">>} - ], - {Encoded, _State} = hackney_hpack:encode(Headers), - {Decoded, _} = hackney_hpack:decode(iolist_to_binary(Encoded)), - ?assertEqual(Headers, Decoded). - -%%==================================================================== -%% Error Handling Tests -%%==================================================================== - -%% Test DATA frame on stream 0 (invalid) -parse_data_stream_zero_test() -> - %% Manually construct invalid DATA frame with stream ID 0 - InvalidFrame = <<0, 0, 5, 0, 0, 0, 0, 0, 0, "hello">>, - ?assertMatch({connection_error, protocol_error, _}, - hackney_http2:parse(InvalidFrame)). - -%% Test HEADERS frame on stream 0 (invalid) -parse_headers_stream_zero_test() -> - InvalidFrame = <<0, 0, 5, 1, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5>>, - ?assertMatch({connection_error, protocol_error, _}, - hackney_http2:parse(InvalidFrame)). - -%% Test WINDOW_UPDATE with zero increment (invalid) -parse_window_update_zero_increment_test() -> - InvalidFrame = <<4:24, 8:8, 0:9, 0:31, 0:1, 0:31>>, - ?assertMatch({connection_error, protocol_error, _}, - hackney_http2:parse(InvalidFrame)). - -%%==================================================================== -%% All Frame Types Test Suite -%%==================================================================== - -all_frame_types_test_() -> - [ - {"Connection preface", fun parse_connection_preface_test/0}, - {"DATA frame", fun parse_data_frame_test/0}, - {"DATA frame with fin", fun parse_data_frame_with_fin_test/0}, - {"HEADERS frame", fun parse_headers_frame_test/0}, - {"SETTINGS frame", fun parse_settings_frame_test/0}, - {"SETTINGS ACK", fun parse_settings_ack_test/0}, - {"PING frame", fun parse_ping_frame_test/0}, - {"PING ACK", fun parse_ping_ack_test/0}, - {"GOAWAY frame", fun parse_goaway_frame_test/0}, - {"WINDOW_UPDATE (connection)", fun parse_window_update_connection_test/0}, - {"WINDOW_UPDATE (stream)", fun parse_window_update_stream_test/0}, - {"RST_STREAM frame", fun parse_rst_stream_test/0}, - {"PRIORITY frame", fun parse_priority_test/0} - ]. - -hpack_test_suite_test_() -> - [ - {"HPACK init", fun hpack_init_test/0}, - {"HPACK init with max size", fun hpack_init_with_max_size_test/0}, - {"HPACK encode/decode roundtrip", fun hpack_encode_decode_test/0}, - {"HPACK custom headers", fun hpack_custom_headers_test/0}, - {"HPACK static table", fun hpack_static_table_test/0}, - {"HPACK no huffman", fun hpack_no_huffman_test/0}, - {"HPACK dynamic table", fun hpack_dynamic_table_test/0}, - {"HPACK set max size", fun hpack_set_max_size_test/0}, - {"HPACK response headers", fun hpack_response_headers_test/0} - ]. diff --git a/test/hackney_pool_h2_tests.erl b/test/hackney_pool_h2_tests.erl deleted file mode 100644 index 62e8c21b..00000000 --- a/test/hackney_pool_h2_tests.erl +++ /dev/null @@ -1,168 +0,0 @@ -%%% -*- erlang -*- -%%% -%%% This file is part of hackney released under the Apache 2 license. -%%% See the NOTICE for more information. -%%% -%%% Copyright (c) 2024-2026 Benoit Chesneau -%%% -%%% @doc Tests for HTTP/2 connection pooling in hackney_pool. - --module(hackney_pool_h2_tests). - --include_lib("eunit/include/eunit.hrl"). - -%%==================================================================== -%% Test Setup -%%==================================================================== - -setup() -> - {ok, _} = application:ensure_all_started(hackney), - ok. - -cleanup(_) -> - hackney_pool:unregister_h2_all(), - hackney_conn_sup:stop_all(), - ok. - -%%==================================================================== -%% HTTP/2 Pool Tests -%%==================================================================== - -h2_pool_test_() -> - { - "HTTP/2 pool tests", - { - setup, - fun setup/0, fun cleanup/1, - [ - {"checkout_h2 returns none when no connection", fun test_h2_checkout_none/0}, - {"register_h2 and checkout_h2", fun test_h2_register_checkout/0}, - {"unregister_h2 removes connection", fun test_h2_unregister/0}, - {"connection death cleans h2_connections", fun test_h2_connection_death/0} - ] - } - }. - -test_h2_checkout_none() -> - %% Without any registered H2 connection, checkout should return none - Result = hackney_pool:checkout_h2("test.example.com", 443, hackney_ssl, []), - ?assertEqual(none, Result). - -test_h2_register_checkout() -> - %% Start a dummy process to act as a connection - DummyConn = spawn(fun() -> receive stop -> ok end end), - - %% Register it as an H2 connection - ok = hackney_pool:register_h2("h2test.example.com", 443, hackney_ssl, DummyConn, []), - - %% Wait a bit for the async cast to process - timer:sleep(50), - - %% Checkout should now return the connection - Result = hackney_pool:checkout_h2("h2test.example.com", 443, hackney_ssl, []), - ?assertEqual({ok, DummyConn}, Result), - - %% Cleanup - DummyConn ! stop. - -test_h2_unregister() -> - %% Start a dummy process - DummyConn = spawn(fun() -> receive stop -> ok end end), - - %% Register it - ok = hackney_pool:register_h2("h2unreg.example.com", 443, hackney_ssl, DummyConn, []), - timer:sleep(50), - - %% Verify it's there - ?assertEqual({ok, DummyConn}, hackney_pool:checkout_h2("h2unreg.example.com", 443, hackney_ssl, [])), - - %% Unregister - ok = hackney_pool:unregister_h2(DummyConn, []), - timer:sleep(50), - - %% Should be gone now - ?assertEqual(none, hackney_pool:checkout_h2("h2unreg.example.com", 443, hackney_ssl, [])), - - %% Cleanup - DummyConn ! stop. - -test_h2_connection_death() -> - %% Start a dummy process - DummyConn = spawn(fun() -> receive stop -> ok end end), - - %% Register it - ok = hackney_pool:register_h2("h2death.example.com", 443, hackney_ssl, DummyConn, []), - timer:sleep(50), - - %% Verify it's there - ?assertEqual({ok, DummyConn}, hackney_pool:checkout_h2("h2death.example.com", 443, hackney_ssl, [])), - - %% Kill the process - DummyConn ! stop, - timer:sleep(100), - - %% Pool should receive DOWN message and clean up - checkout returns none - ?assertEqual(none, hackney_pool:checkout_h2("h2death.example.com", 443, hackney_ssl, [])). - -%%==================================================================== -%% Multiplexing Tests -%%==================================================================== - -h2_multiplexing_test_() -> - { - "HTTP/2 multiplexing tests", - { - setup, - fun setup/0, fun cleanup/1, - [ - {"multiple callers get same connection", fun test_h2_multiplexing/0}, - {"different hosts get different connections", fun test_h2_different_hosts/0} - ] - } - }. - -test_h2_multiplexing() -> - %% Start a dummy connection process - DummyConn = spawn(fun() -> receive stop -> ok end end), - - %% Register it - ok = hackney_pool:register_h2("h2mux.example.com", 443, hackney_ssl, DummyConn, []), - timer:sleep(50), - - %% Multiple checkouts should return the same connection (multiplexing) - {ok, Conn1} = hackney_pool:checkout_h2("h2mux.example.com", 443, hackney_ssl, []), - {ok, Conn2} = hackney_pool:checkout_h2("h2mux.example.com", 443, hackney_ssl, []), - {ok, Conn3} = hackney_pool:checkout_h2("h2mux.example.com", 443, hackney_ssl, []), - - %% All should be the same connection (multiplexed) - ?assertEqual(DummyConn, Conn1), - ?assertEqual(DummyConn, Conn2), - ?assertEqual(DummyConn, Conn3), - ?assertEqual(Conn1, Conn2), - ?assertEqual(Conn2, Conn3), - - %% Cleanup - DummyConn ! stop. - -test_h2_different_hosts() -> - %% Start two dummy connections - Conn1 = spawn(fun() -> receive stop -> ok end end), - Conn2 = spawn(fun() -> receive stop -> ok end end), - - %% Register them for different hosts - ok = hackney_pool:register_h2("host1.example.com", 443, hackney_ssl, Conn1, []), - ok = hackney_pool:register_h2("host2.example.com", 443, hackney_ssl, Conn2, []), - timer:sleep(50), - - %% Checkout for host1 should return Conn1 - ?assertEqual({ok, Conn1}, hackney_pool:checkout_h2("host1.example.com", 443, hackney_ssl, [])), - - %% Checkout for host2 should return Conn2 - ?assertEqual({ok, Conn2}, hackney_pool:checkout_h2("host2.example.com", 443, hackney_ssl, [])), - - %% They should be different - ?assertNotEqual(Conn1, Conn2), - - %% Cleanup - Conn1 ! stop, - Conn2 ! stop.