1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20%% Purpose : Assembler for threaded Beam. 21 22-module(beam_asm). 23 24-export([module/4]). 25-export([encode/2]). 26 27-export_type([fail/0,label/0,reg/0,reg_num/0,src/0,module_code/0,function_name/0]). 28 29-import(lists, [map/2,member/2,keymember/3,duplicate/2,splitwith/2]). 30-include("beam_opcodes.hrl"). 31 32%% Common types for describing operands for BEAM instructions. 33-type reg_num() :: 0..1023. 34-type reg() :: {'x',reg_num()} | {'y',reg_num()}. 35-type src() :: reg() | 36 {'literal',term()} | 37 {'atom',atom()} | 38 {'integer',integer()} | 39 'nil' | 40 {'float',float()}. 41-type label() :: pos_integer(). 42-type fail() :: {'f',label() | 0}. 43 44%% asm_instruction() describes only the instructions that 45%% are used in BEAM files (as opposed to internal instructions 46%% used only during optimization). 47 48-type asm_instruction() :: atom() | tuple(). 49 50-type function_name() :: atom(). 51 52-type asm_function() :: 53 {'function',function_name(),arity(),label(),[asm_instruction()]}. 54 55-type module_code() :: 56 {module(),[_],[_],[asm_function()],pos_integer()}. 57 58-spec module(module_code(), [{binary(), binary()}], [{atom(),term()}], [compile:option()]) -> 59 {'ok',binary()}. 60 61module(Code, ExtraChunks, CompileInfo, CompilerOpts) -> 62 {ok,assemble(Code, ExtraChunks, CompileInfo, CompilerOpts)}. 63 64assemble({Mod,Exp0,Attr0,Asm0,NumLabels}, ExtraChunks, CompileInfo, CompilerOpts) -> 65 {1,Dict0} = beam_dict:atom(Mod, beam_dict:new()), 66 {0,Dict1} = beam_dict:fname(atom_to_list(Mod) ++ ".erl", Dict0), 67 Dict2 = shared_fun_wrappers(CompilerOpts, Dict1), 68 NumFuncs = length(Asm0), 69 {Asm,Attr} = on_load(Asm0, Attr0), 70 Exp = cerl_sets:from_list(Exp0), 71 {Code,Dict} = assemble_1(Asm, Exp, Dict2, []), 72 build_file(Code, Attr, Dict, NumLabels, NumFuncs, 73 ExtraChunks, CompileInfo, CompilerOpts). 74 75shared_fun_wrappers(Opts, Dict) -> 76 case proplists:get_bool(no_shared_fun_wrappers, Opts) of 77 false -> 78 %% The compiler in OTP 23 depends on the on the loader 79 %% using the new indices in funs and being able to have 80 %% multiple make_fun2 instructions referring to the same 81 %% fun entry. Artificially set the highest opcode for the 82 %% module to ensure that it cannot be loaded in OTP 22 83 %% and earlier. 84 Swap = beam_opcodes:opcode(swap, 2), 85 beam_dict:opcode(Swap, Dict); 86 true -> 87 %% Fun wrappers are not shared for compatibility with a 88 %% previous OTP release. 89 Dict 90 end. 91 92on_load(Fs0, Attr0) -> 93 case proplists:get_value(on_load, Attr0) of 94 undefined -> 95 {Fs0,Attr0}; 96 [{Name,0}] -> 97 Fs = map(fun({function,N,0,Entry,Is0}) when N =:= Name -> 98 Is = insert_on_load_instruction(Is0, Entry), 99 {function,N,0,Entry,Is}; 100 (F) -> 101 F 102 end, Fs0), 103 Attr = proplists:delete(on_load, Attr0), 104 {Fs,Attr} 105 end. 106 107insert_on_load_instruction(Is0, Entry) -> 108 {Bef,[{label,Entry}=El|Is]} = 109 splitwith(fun({label,L}) when L =:= Entry -> false; 110 (_) -> true 111 end, Is0), 112 Bef ++ [El,on_load|Is]. 113 114assemble_1([{function,Name,Arity,Entry,Asm}|T], Exp, Dict0, Acc) -> 115 Dict1 = case cerl_sets:is_element({Name,Arity}, Exp) of 116 true -> 117 beam_dict:export(Name, Arity, Entry, Dict0); 118 false -> 119 beam_dict:local(Name, Arity, Entry, Dict0) 120 end, 121 {Code, Dict2} = assemble_function(Asm, Acc, Dict1), 122 assemble_1(T, Exp, Dict2, Code); 123assemble_1([], _Exp, Dict0, Acc) -> 124 {IntCodeEnd,Dict1} = make_op(int_code_end, Dict0), 125 {list_to_binary(lists:reverse(Acc, [IntCodeEnd])),Dict1}. 126 127assemble_function([H|T], Acc, Dict0) -> 128 {Code, Dict} = make_op(H, Dict0), 129 assemble_function(T, [Code| Acc], Dict); 130assemble_function([], Code, Dict) -> 131 {Code, Dict}. 132 133build_file(Code, Attr, Dict, NumLabels, NumFuncs, ExtraChunks, CompileInfo, CompilerOpts) -> 134 %% Create the code chunk. 135 136 CodeChunk = chunk(<<"Code">>, 137 <<16:32, 138 (beam_opcodes:format_number()):32, 139 (beam_dict:highest_opcode(Dict)):32, 140 NumLabels:32, 141 NumFuncs:32>>, 142 Code), 143 144 %% Create the atom table chunk. 145 AtomEncoding = atom_encoding(CompilerOpts), 146 {NumAtoms, AtomTab} = beam_dict:atom_table(Dict, AtomEncoding), 147 AtomChunk = chunk(atom_chunk_name(AtomEncoding), <<NumAtoms:32>>, AtomTab), 148 149 %% Create the import table chunk. 150 151 {NumImps, ImpTab0} = beam_dict:import_table(Dict), 152 Imp = flatten_imports(ImpTab0), 153 ImportChunk = chunk(<<"ImpT">>, <<NumImps:32>>, Imp), 154 155 %% Create the export table chunk. 156 157 {NumExps, ExpTab0} = beam_dict:export_table(Dict), 158 Exp = flatten_exports(ExpTab0), 159 ExpChunk = chunk(<<"ExpT">>, <<NumExps:32>>, Exp), 160 161 %% Create the local function table chunk. 162 163 {NumLocals, Locals} = beam_dict:local_table(Dict), 164 Loc = flatten_exports(Locals), 165 LocChunk = chunk(<<"LocT">>, <<NumLocals:32>>, Loc), 166 167 %% Create the string table chunk. 168 169 {_,StringTab} = beam_dict:string_table(Dict), 170 StringChunk = chunk(<<"StrT">>, StringTab), 171 172 %% Create the fun table chunk. It is important not to build an empty chunk, 173 %% as that would change the MD5. 174 175 LambdaChunk = case beam_dict:lambda_table(Dict) of 176 {0,[]} -> []; 177 {NumLambdas,LambdaTab} -> 178 chunk(<<"FunT">>, <<NumLambdas:32>>, LambdaTab) 179 end, 180 181 %% Create the literal table chunk. It is important not to build an empty chunk, 182 %% as that would change the MD5. 183 184 LiteralChunk = case beam_dict:literal_table(Dict) of 185 {0,[]} -> []; 186 {NumLiterals,LitTab0} -> 187 LitTab1 = [<<NumLiterals:32>>,LitTab0], 188 LitTab = zlib:compress(LitTab1), 189 chunk(<<"LitT">>, <<(iolist_size(LitTab1)):32>>, 190 LitTab) 191 end, 192 193 %% Create the line chunk. 194 195 LineChunk = chunk(<<"Line">>, build_line_table(Dict)), 196 197 %% Create the attributes and compile info chunks. 198 199 Essentials0 = [AtomChunk,CodeChunk,StringChunk,ImportChunk, 200 ExpChunk,LambdaChunk,LiteralChunk], 201 Essentials1 = [iolist_to_binary(C) || C <- Essentials0], 202 MD5 = module_md5(Essentials1), 203 Essentials = finalize_fun_table(Essentials1, MD5), 204 {Attributes,Compile} = build_attributes(Attr, CompileInfo, MD5), 205 AttrChunk = chunk(<<"Attr">>, Attributes), 206 CompileChunk = chunk(<<"CInf">>, Compile), 207 208 %% Compile all extra chunks. 209 210 CheckedChunks = [chunk(Key, Value) || {Key, Value} <- ExtraChunks], 211 212 %% Create IFF chunk. 213 214 Chunks = case member(slim, CompilerOpts) of 215 true -> 216 [Essentials,AttrChunk]; 217 false -> 218 [Essentials,LocChunk,AttrChunk, 219 CompileChunk,CheckedChunks,LineChunk] 220 end, 221 build_form(<<"BEAM">>, Chunks). 222 223atom_encoding(Opts) -> 224 case proplists:get_bool(no_utf8_atoms, Opts) of 225 false -> utf8; 226 true -> latin1 227 end. 228 229atom_chunk_name(utf8) -> <<"AtU8">>; 230atom_chunk_name(latin1) -> <<"Atom">>. 231 232%% finalize_fun_table(Essentials, MD5) -> FinalizedEssentials 233%% Update the 'old_uniq' field in the entry for each fun in the 234%% 'FunT' chunk. We'll use part of the MD5 for the module as a 235%% unique value. 236 237finalize_fun_table(Essentials, MD5) -> 238 [finalize_fun_table_1(E, MD5) || E <- Essentials]. 239 240finalize_fun_table_1(<<"FunT",Keep:8/binary,Table0/binary>>, MD5) -> 241 <<Uniq:27,_:101/bits>> = MD5, 242 Table = finalize_fun_table_2(Table0, Uniq, <<>>), 243 <<"FunT",Keep/binary,Table/binary>>; 244finalize_fun_table_1(Chunk, _) -> Chunk. 245 246finalize_fun_table_2(<<Keep:20/binary,0:32,T/binary>>, Uniq, Acc) -> 247 finalize_fun_table_2(T, Uniq, <<Acc/binary,Keep/binary,Uniq:32>>); 248finalize_fun_table_2(<<>>, _, Acc) -> Acc. 249 250%% Build an IFF form. 251 252build_form(Id, Chunks0) when byte_size(Id) =:= 4, is_list(Chunks0) -> 253 Chunks = list_to_binary(Chunks0), 254 Size = byte_size(Chunks), 255 0 = Size rem 4, % Assertion: correct padding? 256 <<"FOR1",(Size+4):32,Id/binary,Chunks/binary>>. 257 258%% Build a correctly padded chunk (with no sub-header). 259 260chunk(Id, Contents) when byte_size(Id) =:= 4, is_binary(Contents) -> 261 Size = byte_size(Contents), 262 [<<Id/binary,Size:32>>,Contents|pad(Size)]. 263 264%% Build a correctly padded chunk (with a sub-header). 265 266chunk(Id, Head, Contents) when byte_size(Id) =:= 4, is_binary(Head), is_binary(Contents) -> 267 Size = byte_size(Head)+byte_size(Contents), 268 [<<Id/binary,Size:32,Head/binary>>,Contents|pad(Size)]; 269chunk(Id, Head, Contents) when is_list(Contents) -> 270 chunk(Id, Head, list_to_binary(Contents)). 271 272pad(Size) -> 273 case Size rem 4 of 274 0 -> []; 275 Rem -> duplicate(4 - Rem, 0) 276 end. 277 278flatten_exports(Exps) -> 279 list_to_binary(map(fun({F,A,L}) -> <<F:32,A:32,L:32>> end, Exps)). 280 281flatten_imports(Imps) -> 282 list_to_binary(map(fun({M,F,A}) -> <<M:32,F:32,A:32>> end, Imps)). 283 284build_attributes(Attr, Compile, MD5) -> 285 AttrBinary = term_to_binary(set_vsn_attribute(Attr, MD5)), 286 CompileBinary = term_to_binary([{version,?COMPILER_VSN}|Compile]), 287 {AttrBinary,CompileBinary}. 288 289build_line_table(Dict) -> 290 {NumLineInstrs,NumFnames0,Fnames0,NumLines,Lines0} = 291 beam_dict:line_table(Dict), 292 NumFnames = NumFnames0 - 1, 293 [_|Fnames1] = Fnames0, 294 Fnames2 = [unicode:characters_to_binary(F) || F <- Fnames1], 295 Fnames = << <<(byte_size(F)):16,F/binary>> || F <- Fnames2 >>, 296 Lines1 = encode_line_items(Lines0, 0), 297 Lines = iolist_to_binary(Lines1), 298 Ver = 0, 299 Bits = 0, 300 <<Ver:32,Bits:32,NumLineInstrs:32,NumLines:32,NumFnames:32, 301 Lines/binary,Fnames/binary>>. 302 303%% encode_line_items([{FnameIndex,Line}], PrevFnameIndex) 304%% Encode the line items compactly. Tag the FnameIndex with 305%% an 'a' tag (atom) and only include it when it has changed. 306%% Tag the line numbers with an 'i' (integer) tag. 307 308encode_line_items([{F,L}|T], F) -> 309 [encode(?tag_i, L)|encode_line_items(T, F)]; 310encode_line_items([{F,L}|T], _) -> 311 [encode(?tag_a, F),encode(?tag_i, L)|encode_line_items(T, F)]; 312encode_line_items([], _) -> []. 313 314%% 315%% If the attributes contains no 'vsn' attribute, we'll insert one 316%% with an MD5 "checksum" calculated on the code as its value. 317%% We'll not change an existing 'vsn' attribute. 318%% 319 320set_vsn_attribute(Attr, MD5) -> 321 case keymember(vsn, 1, Attr) of 322 true -> Attr; 323 false -> 324 <<Number:128>> = MD5, 325 [{vsn,[Number]}|Attr] 326 end. 327 328module_md5(Essentials0) -> 329 Essentials = filter_essentials(Essentials0), 330 erlang:md5(Essentials). 331 332%% filter_essentials([Chunk]) -> [Chunk'] 333%% Filter essentials so that we obtain the same MD5 as code:module_md5/1 and 334%% beam_lib:md5/1 would calculate for this module. Note that at this 335%% point, the 'old_uniq' entry for each fun in the 'FunT' chunk is zeroed, 336%% so there is no need to go through the 'FunT' chunk. 337 338filter_essentials([<<_Tag:4/binary,Sz:32,Data:Sz/binary,_Padding/binary>>|T]) -> 339 [Data|filter_essentials(T)]; 340filter_essentials([<<>>|T]) -> 341 filter_essentials(T); 342filter_essentials([]) -> []. 343 344bif_type(fnegate, 1) -> {op,fnegate}; 345bif_type(fadd, 2) -> {op,fadd}; 346bif_type(fsub, 2) -> {op,fsub}; 347bif_type(fmul, 2) -> {op,fmul}; 348bif_type(fdiv, 2) -> {op,fdiv}; 349bif_type(_, 1) -> bif1; 350bif_type(_, 2) -> bif2. 351 352make_op({'%',_}, Dict) -> 353 {[],Dict}; 354make_op({line,Location}, Dict0) -> 355 {Index,Dict} = beam_dict:line(Location, Dict0), 356 encode_op(line, [Index], Dict); 357make_op({bif, Bif, {f,_}, [], Dest}, Dict) -> 358 %% BIFs without arguments cannot fail. 359 encode_op(bif0, [{extfunc, erlang, Bif, 0}, Dest], Dict); 360make_op({bif, raise, _Fail, [_A1,_A2] = Args, _Dest}, Dict) -> 361 encode_op(raise, Args, Dict); 362make_op({bif,Bif,Fail,Args,Dest}, Dict) -> 363 Arity = length(Args), 364 case bif_type(Bif, Arity) of 365 {op,Op} -> 366 make_op(list_to_tuple([Op,Fail|Args++[Dest]]), Dict); 367 BifOp when is_atom(BifOp) -> 368 encode_op(BifOp, [Fail,{extfunc,erlang,Bif,Arity}|Args++[Dest]], 369 Dict) 370 end; 371make_op({gc_bif,Bif,Fail,Live,Args,Dest}, Dict) -> 372 Arity = length(Args), 373 BifOp = case Arity of 374 1 -> gc_bif1; 375 2 -> gc_bif2; 376 3 -> gc_bif3 377 end, 378 encode_op(BifOp, [Fail,Live,{extfunc,erlang,Bif,Arity}|Args++[Dest]],Dict); 379make_op({bs_add=Op,Fail,[Src1,Src2,Unit],Dest}, Dict) -> 380 encode_op(Op, [Fail,Src1,Src2,Unit,Dest], Dict); 381make_op({test,Cond,Fail,Src,{list,_}=Ops}, Dict) -> 382 encode_op(Cond, [Fail,Src,Ops], Dict); 383make_op({test,Cond,Fail,Ops}, Dict) when is_list(Ops) -> 384 encode_op(Cond, [Fail|Ops], Dict); 385make_op({test,Cond,Fail,Live,[Op|Ops],Dst}, Dict) when is_list(Ops) -> 386 encode_op(Cond, [Fail,Op,Live|Ops++[Dst]], Dict); 387make_op({make_fun2,{f,Lbl},_Index,_OldUniq,NumFree}, Dict0) -> 388 {Fun,Dict} = beam_dict:lambda(Lbl, NumFree, Dict0), 389 make_op({make_fun2,Fun}, Dict); 390make_op({kill,Y}, Dict) -> 391 make_op({init,Y}, Dict); 392make_op({Name,Arg1}, Dict) -> 393 encode_op(Name, [Arg1], Dict); 394make_op({Name,Arg1,Arg2}, Dict) -> 395 encode_op(Name, [Arg1,Arg2], Dict); 396make_op({Name,Arg1,Arg2,Arg3}, Dict) -> 397 encode_op(Name, [Arg1,Arg2,Arg3], Dict); 398make_op({Name,Arg1,Arg2,Arg3,Arg4}, Dict) -> 399 encode_op(Name, [Arg1,Arg2,Arg3,Arg4], Dict); 400make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5}, Dict) -> 401 encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5], Dict); 402make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6}, Dict) -> 403 encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5,Arg6], Dict); 404%% make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7}, Dict) -> 405%% encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7], Dict); 406make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7,Arg8}, Dict) -> 407 encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7,Arg8], Dict); 408make_op(Op, Dict) when is_atom(Op) -> 409 encode_op(Op, [], Dict). 410 411encode_op(Name, Args, Dict0) when is_atom(Name) -> 412 Op = beam_opcodes:opcode(Name, length(Args)), 413 Dict = beam_dict:opcode(Op, Dict0), 414 encode_op_1(Args, Dict, Op). 415 416encode_op_1([A0|As], Dict0, Acc) -> 417 {A,Dict} = encode_arg(A0, Dict0), 418 encode_op_1(As, Dict, [Acc,A]); 419encode_op_1([], Dict, Acc) -> {Acc,Dict}. 420 421encode_arg({x, X}, Dict) when is_integer(X), X >= 0 -> 422 {encode(?tag_x, X), Dict}; 423encode_arg({y, Y}, Dict) when is_integer(Y), Y >= 0 -> 424 {encode(?tag_y, Y), Dict}; 425encode_arg({atom, Atom}, Dict0) when is_atom(Atom) -> 426 {Index, Dict} = beam_dict:atom(Atom, Dict0), 427 {encode(?tag_a, Index), Dict}; 428encode_arg({integer, N}, Dict) -> 429 %% Conservatively assume that all integers whose absolute 430 %% value is greater than 1 bsl 128 will be bignums in 431 %% the runtime system. 432 if 433 N >= 1 bsl 128 -> 434 encode_literal(N, Dict); 435 N =< -(1 bsl 128) -> 436 encode_literal(N, Dict); 437 true -> 438 {encode(?tag_i, N), Dict} 439 end; 440encode_arg(nil, Dict) -> 441 {encode(?tag_a, 0), Dict}; 442encode_arg({f, W}, Dict) -> 443 {encode(?tag_f, W), Dict}; 444%% encode_arg({'char', C}, Dict) -> 445%% {encode(?tag_h, C), Dict}; 446encode_arg({string, BinString}, Dict0) when is_binary(BinString) -> 447 {Offset, Dict} = beam_dict:string(BinString, Dict0), 448 {encode(?tag_u, Offset), Dict}; 449encode_arg({extfunc, M, F, A}, Dict0) -> 450 {Index, Dict} = beam_dict:import(M, F, A, Dict0), 451 {encode(?tag_u, Index), Dict}; 452encode_arg({list, List}, Dict0) -> 453 {L, Dict} = encode_list(List, Dict0, []), 454 {[encode(?tag_z, 1), encode(?tag_u, length(List))|L], Dict}; 455encode_arg({float, Float}, Dict) when is_float(Float) -> 456 encode_literal(Float, Dict); 457encode_arg({fr,Fr}, Dict) -> 458 {[encode(?tag_z, 2),encode(?tag_u, Fr)], Dict}; 459encode_arg({field_flags,Flags0}, Dict) -> 460 Flags = lists:foldl(fun (F, S) -> S bor flag_to_bit(F) end, 0, Flags0), 461 {encode(?tag_u, Flags), Dict}; 462encode_arg({alloc,List}, Dict) -> 463 encode_alloc_list(List, Dict); 464encode_arg({literal,Lit}, Dict) -> 465 if 466 Lit =:= [] -> 467 encode_arg(nil, Dict); 468 is_atom(Lit) -> 469 encode_arg({atom,Lit}, Dict); 470 is_integer(Lit) -> 471 encode_arg({integer,Lit}, Dict); 472 true -> 473 encode_literal(Lit, Dict) 474 end; 475encode_arg(Int, Dict) when is_integer(Int) -> 476 {encode(?tag_u, Int),Dict}. 477 478encode_literal(Literal, Dict0) -> 479 {Index,Dict} = beam_dict:literal(Literal, Dict0), 480 {[encode(?tag_z, 4),encode(?tag_u, Index)],Dict}. 481 482%%flag_to_bit(aligned) -> 16#01; %% No longer useful. 483flag_to_bit(little) -> 16#02; 484flag_to_bit(big) -> 16#00; 485flag_to_bit(signed) -> 16#04; 486flag_to_bit(unsigned)-> 16#00; 487%%flag_to_bit(exact) -> 16#08; 488flag_to_bit(native) -> 16#10; 489flag_to_bit({anno,_}) -> 0. 490 491encode_list([H|T], Dict0, Acc) when not is_list(H) -> 492 {Enc,Dict} = encode_arg(H, Dict0), 493 encode_list(T, Dict, [Acc,Enc]); 494encode_list([], Dict, Acc) -> {Acc,Dict}. 495 496encode_alloc_list(L0, Dict0) -> 497 {Bin,Dict} = encode_alloc_list_1(L0, Dict0, []), 498 {[encode(?tag_z, 3),encode(?tag_u, length(L0)),Bin],Dict}. 499 500encode_alloc_list_1([{words,Words}|T], Dict, Acc0) -> 501 Acc = [Acc0,encode(?tag_u, 0),encode(?tag_u, Words)], 502 encode_alloc_list_1(T, Dict, Acc); 503encode_alloc_list_1([{floats,Floats}|T], Dict, Acc0) -> 504 Acc = [Acc0,encode(?tag_u, 1),encode(?tag_u, Floats)], 505 encode_alloc_list_1(T, Dict, Acc); 506encode_alloc_list_1([], Dict, Acc) -> 507 {iolist_to_binary(Acc),Dict}. 508 509-spec encode(non_neg_integer(), integer()) -> iolist() | integer(). 510 511encode(Tag, N) when N < 0 -> 512 encode1(Tag, negative_to_bytes(N)); 513encode(Tag, N) when N < 16 -> 514 (N bsl 4) bor Tag; 515encode(Tag, N) when N < 16#800 -> 516 [((N bsr 3) band 2#11100000) bor Tag bor 2#00001000, N band 16#ff]; 517encode(Tag, N) -> 518 encode1(Tag, to_bytes(N)). 519 520encode1(Tag, Bytes) -> 521 case iolist_size(Bytes) of 522 Num when 2 =< Num, Num =< 8 -> 523 [((Num-2) bsl 5) bor 2#00011000 bor Tag| Bytes]; 524 Num when 8 < Num -> 525 [2#11111000 bor Tag, encode(?tag_u, Num-9)| Bytes] 526 end. 527 528to_bytes(N) -> 529 Bin = binary:encode_unsigned(N), 530 case Bin of 531 <<0:1,_/bits>> -> Bin; 532 <<1:1,_/bits>> -> [0,Bin] 533 end. 534 535negative_to_bytes(N) when N >= -16#8000 -> 536 <<N:16>>; 537negative_to_bytes(N) -> 538 Bytes = byte_size(binary:encode_unsigned(-N)), 539 Bin = <<N:Bytes/unit:8>>, 540 case Bin of 541 <<0:1,_/bits>> -> [16#ff,Bin]; 542 <<1:1,_/bits>> -> Bin 543 end. 544