Packages
amqp10_common
4.0.3
4.2.1
4.2.0
retired
4.1.6
4.0.3
4.0.3-rc.1
4.0.2
4.0.2-rc.2
4.0.2-rc.1
4.0.1
4.0.0
4.0.0-rc.2
4.0.0-rc.1
3.13.7
3.13.6
3.13.5
3.13.4
3.13.3
3.13.2
3.13.2-rc.1
3.13.1
3.13.0
3.13.0-rc.6
3.13.0-rc.5
3.13.0-rc.4
3.13.0-rc.3
3.13.0-rc.2
3.13.0-rc.1
3.12.14
3.12.13
3.12.12
3.12.11
3.12.10
3.12.9
3.12.8
3.12.7
3.12.6
3.12.5
3.12.4
3.12.3
3.12.2
3.12.1
3.12.0
3.12.0-rc.4
3.12.0-rc.3
3.12.0-rc.2
3.12.0-rc.1
3.11.28
3.11.27
3.11.26
3.11.25
3.11.24
3.11.23
3.11.22
3.11.21
3.11.20
3.11.19
3.11.18
3.11.17
3.11.16
3.11.15
3.11.14
3.11.13
3.11.12
3.11.11
3.11.10
3.11.9
3.11.8
3.11.7
3.11.6
3.11.5
3.11.4
3.11.3
3.11.2
3.11.1
3.11.0
3.11.0-rc.2
3.11.0-rc.1
3.11.0-1
3.10.25
3.10.24
3.10.23
3.10.22
3.10.21
3.10.20
3.10.19
3.10.18
3.10.17
3.10.16
3.10.15
3.10.14
3.10.13
3.10.12
3.10.11
3.10.10
3.10.9
3.10.8
3.10.7
3.10.6
3.10.5
3.10.4
3.10.3
3.10.2
retired
3.10.2-1
3.10.1
retired
3.10.0
retired
3.10.0-rc.6
3.10.0-rc.5
3.10.0-rc.1
3.9.29
3.9.28
3.9.27
3.9.26
3.9.25
3.9.24
3.9.23
3.9.22
3.9.21
3.9.20
3.9.19
3.9.18
retired
3.9.17
3.9.16
3.9.15
3.8.35
3.8.34
3.8.33
3.8.32
3.8.31
3.8.30
0.0.0-rc.1
Modules shared by rabbitmq-amqp1.0 and rabbitmq-amqp1.0-client
Current section
Files
Jump to
Current section
Files
src/amqp10_binary_generator.erl
%% This Source Code Form is subject to the terms of the Mozilla Public
%% License, v. 2.0. If a copy of the MPL was not distributed with this
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
%%
%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
%%
-module(amqp10_binary_generator).
-export([generate/1, build_frame/2, build_frame/3,
build_heartbeat_frame/0]).
-include("amqp10_framing.hrl").
-type signed_byte() :: -128 .. 127.
-type amqp10_ctor() :: ubyte | ushort | byte | short | int | uing | float |
double |
char | timestamp | uuid | utf8 | symbol | binary |
list | map | array |
{described, amqp10_type(), amqp10_ctor()}.
-type amqp10_prim() ::
null |
boolean() |
{boolean, boolean()} |
{ubyte, byte()} |
{ushort, non_neg_integer()} |
{uint, non_neg_integer()} |
{ulong, non_neg_integer()} |
{byte, signed_byte()} |
{short, integer()} |
{int, integer()} |
{float, float()} |
{double, float()} |
{char, binary()} |
{timestamp, integer()} |
{uuid, binary()} |
{utf8, binary()} |
{symbol, binary()} |
{binary, binary()} |
{list, [amqp10_type()]} |
{map, [{amqp10_prim(), amqp10_prim()}]} |
{array, amqp10_ctor(), [amqp10_type()]}.
-type amqp10_described() ::
{described, amqp10_type(), amqp10_prim()}.
-type amqp10_type() ::
amqp10_prim() | amqp10_described().
-export_type([
amqp10_ctor/0,
amqp10_type/0,
amqp10_prim/0,
amqp10_described/0
]).
-define(AMQP_FRAME_TYPE, 0).
-define(DOFF, 2).
-define(VAR_1_LIMIT, 16#FF).
-spec build_frame(non_neg_integer(), iolist()) -> iolist().
build_frame(Channel, Body) ->
build_frame(Channel, ?AMQP_FRAME_TYPE, Body).
-spec build_frame(non_neg_integer(), non_neg_integer(), iolist()) -> iolist().
build_frame(Channel, FrameType, Body) ->
Size = iolist_size(Body) + 8, % frame header and no extension
[<<Size:32, 2:8, FrameType:8, Channel:16>>, Body].
build_heartbeat_frame() ->
%% length is inclusive
<<8:32, ?DOFF:8, ?AMQP_FRAME_TYPE:8, 0:16>>.
-spec generate(amqp10_type()) -> iodata().
generate(Type) ->
case generate1(Type) of
Byte when is_integer(Byte) ->
[Byte];
IoData ->
IoData
end.
generate1({described, Descriptor, Value}) ->
DescBin = generate1(Descriptor),
ValueBin = generate1(Value),
[?DESCRIBED, DescBin, ValueBin];
generate1(null) -> 16#40;
generate1(true) -> 16#41;
generate1(false) -> 16#42;
generate1({boolean, true}) -> [16#56, 16#01];
generate1({boolean, false}) -> [16#56, 16#00];
%% some integral types have a compact encoding as a byte; this is in
%% particular for the descriptors of AMQP types, which have the domain
%% bits set to zero and values < 256.
generate1({ubyte, V}) -> [16#50, V];
generate1({ushort, V}) -> <<16#60,V:16/unsigned>>;
generate1({uint, V}) when V =:= 0 -> 16#43;
generate1({uint, V}) when V < 256 -> [16#52, V];
generate1({uint, V}) -> <<16#70,V:32/unsigned>>;
generate1({ulong, V}) when V =:= 0 -> 16#44;
generate1({ulong, V}) when V < 256 -> [16#53, V];
generate1({ulong, V}) -> <<16#80,V:64/unsigned>>;
generate1({byte, V}) -> <<16#51,V:8/signed>>;
generate1({short, V}) -> <<16#61,V:16/signed>>;
generate1({int, V}) when V<128 andalso V>-129 -> <<16#54,V:8/signed>>;
generate1({int, V}) -> <<16#71,V:32/signed>>;
generate1({long, V}) when V<128 andalso V>-129 -> <<16#55,V:8/signed>>;
generate1({long, V}) -> <<16#81,V:64/signed>>;
generate1({float, V}) -> <<16#72,V:32/float>>;
generate1({double, V}) -> <<16#82,V:64/float>>;
generate1({char,V}) when V>=0 andalso V=<16#10ffff -> <<16#73,V:32>>;
%% AMQP timestamp is "64-bit two's-complement integer representing milliseconds since the unix epoch".
%% For small integers (i.e. values that can be stored in a single word),
%% Erlang uses two’s complement to represent the signed integers.
generate1({timestamp,V}) -> <<16#83,V:64/signed>>;
generate1({uuid, V}) -> <<16#98,V:16/binary>>;
generate1({utf8, V}) when size(V) =< ?VAR_1_LIMIT -> [16#a1, size(V), V];
generate1({utf8, V}) -> [<<16#b1, (size(V)):32>>, V];
generate1({symbol, V}) when size(V) =< ?VAR_1_LIMIT -> [16#a3, size(V), V];
generate1({symbol, V}) -> [<<16#b3, (size(V)):32>>, V];
generate1({binary, V}) ->
Size = iolist_size(V),
case Size =< ?VAR_1_LIMIT of
true ->
[16#a0, Size, V];
false ->
[<<16#b0, Size:32>>, V]
end;
generate1({list, []}) ->
16#45;
generate1({list, List}) ->
Count = length(List),
Compound = lists:map(fun generate1/1, List),
S = iolist_size(Compound),
%% If the list contains less than (256 - 1) elements and if the
%% encoded size (including the encoding of "Count", thus S + 1
%% in the test) is less than 256 bytes, we use the short form.
%% Otherwise, we use the large form.
if Count >= (256 - 1) orelse (S + 1) >= 256 ->
[<<16#d0, (S + 4):32, Count:32>>, Compound];
true ->
[16#c0, S + 1, Count, Compound]
end;
generate1({map, KvList}) ->
Count = length(KvList) * 2,
Compound = lists:map(fun ({Key, Val}) ->
[(generate1(Key)),
(generate1(Val))]
end, KvList),
S = iolist_size(Compound),
%% See generate1({list, ...}) for an explanation of this test.
if Count >= (256 - 1) orelse (S + 1) >= 256 ->
[<<16#d1, (S + 4):32, Count:32>>, Compound];
true ->
[16#c1, S + 1, Count, Compound]
end;
generate1({array, Type, List}) ->
Count = length(List),
Array = [constructor(Type),
[generate2(Type, I) || I <- List]],
S = iolist_size(Array),
%% See generate1({list, ...}) for an explanation of this test.
if Count >= (256 - 1) orelse (S + 1) >= 256 ->
[<<16#f0, (S + 4):32, Count:32>>, Array];
true ->
[16#e0, S + 1, Count, Array]
end;
generate1({as_is, TypeCode, Bin}) ->
<<TypeCode, Bin>>.
constructor(symbol) -> 16#b3;
constructor(ubyte) -> 16#50;
constructor(ushort) -> 16#60;
constructor(short) -> 16#61;
constructor(uint) -> 16#70;
constructor(ulong) -> 16#80;
constructor(byte) -> 16#51;
constructor(int) -> 16#71;
constructor(long) -> 16#81;
constructor(float) -> 16#72;
constructor(double) -> 16#82;
constructor(char) -> 16#73;
constructor(timestamp) -> 16#83;
constructor(uuid) -> 16#98;
constructor(null) -> 16#40;
constructor(boolean) -> 16#56;
constructor(binary) -> 16#b0;
constructor(utf8) -> 16#b1;
constructor(list) -> 16#d0; % use large list type for all array elements
constructor(map) -> 16#d1; % use large map type for all array elements
constructor(array) -> 16#f0; % use large array type for all nested arrays
constructor({described, Descriptor, Primitive}) ->
[16#00, generate1(Descriptor), constructor(Primitive)].
generate2(symbol, {symbol, V}) -> [<<(size(V)):32>>, V];
generate2(utf8, {utf8, V}) -> [<<(size(V)):32>>, V];
generate2(binary, {binary, V}) -> [<<(size(V)):32>>, V];
generate2(boolean, true) -> 16#01;
generate2(boolean, false) -> 16#00;
generate2(boolean, {boolean, true}) -> 16#01;
generate2(boolean, {boolean, false}) -> 16#00;
generate2(null, null) -> 16#40;
generate2(char, {char,V}) when V>=0 andalso V=<16#10ffff -> <<V:32>>;
generate2(ubyte, {ubyte, V}) -> V;
generate2(byte, {byte, V}) -> <<V:8/signed>>;
generate2(ushort, {ushort, V}) -> <<V:16/unsigned>>;
generate2(short, {short, V}) -> <<V:16/signed>>;
generate2(uint, {uint, V}) -> <<V:32/unsigned>>;
generate2(int, {int, V}) -> <<V:32/signed>>;
generate2(ulong, {ulong, V}) -> <<V:64/unsigned>>;
generate2(long, {long, V}) -> <<V:64/signed>>;
generate2(float, {float, V}) -> <<V:32/float>>;
generate2(double, {double, V}) -> <<V:64/float>>;
generate2(timestamp, {timestamp,V}) -> <<V:64/signed>>;
generate2(uuid, {uuid, V}) -> <<V:16/binary>>;
generate2({described, D, P}, {described, D, V}) ->
generate2(P, V);
generate2(list, {list, List}) ->
Count = length(List),
Compound = lists:map(fun generate1/1, List),
S = iolist_size(Compound),
[<<(S + 4):32, Count:32>>, Compound];
generate2(map, {map, KvList}) ->
Count = length(KvList) * 2,
Compound = lists:map(fun ({Key, Val}) ->
[(generate1(Key)),
(generate1(Val))]
end, KvList),
S = iolist_size(Compound),
[<<(S + 4):32, Count:32>>, Compound];
generate2(array, {array, Type, List}) ->
Count = length(List),
Array = [constructor(Type),
[generate2(Type, I) || I <- List]],
S = iolist_size(Array),
[<<(S + 4):32, Count:32>>, Array].