%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
-module(erl_bits).
-export([system_bittypes/0,
system_bitdefault/0,
set_bit_type/2,
as_list/1]).
-include("../include/erl_bits.hrl").
%% Dummies.
-spec system_bitdefault() -> 'no_system_bitdefault'.
system_bitdefault() -> no_system_bitdefault.
-spec system_bittypes() -> 'no_system_types'.
system_bittypes() -> no_system_types.
-spec as_list(#bittype{}) ->
[bt_endian() | bt_sign() | bt_type() | {'unit', 'undefined' | bt_unit()}].
as_list(Bt) ->
[Bt#bittype.type,{unit,Bt#bittype.unit},Bt#bittype.sign,Bt#bittype.endian].
%% XXX: tuple() below stands for what's produced by the parser
%% {integer,L,M} | {var,L,VAR} | {atom,L,ATOM} | {op,L,OP,OP1,OP2} | ...
-type size() :: 'all' | 'unknown' | non_neg_integer() | tuple(). % XXX: REFINE
-type type() :: 'bytes' | 'bitstring' | 'bits'
| bt_type() | bt_endian() | bt_sign()
| {'unit', 'undefined' | bt_unit()}.
-spec set_bit_type('default' | size(), 'default' | [type()]) ->
{'ok', 'undefined' | size(), #bittype{}} |
{'error', {'undefined_bittype', term()}} |
{'error', {'bittype_mismatch', term(), term(), string()}}.
set_bit_type(Size, default) ->
set_bit_type(Size, []);
set_bit_type(Size, TypeList) ->
try
#bittype{type=Type,unit=Unit,sign=Sign,endian=Endian} =
set_bit(TypeList),
apply_defaults(Type, Size, Unit, Sign, Endian)
catch
throw:Error -> Error
end.
set_bit([]) -> #bittype{};
set_bit([H|T]) -> set_bit_1(T, type_to_record(H)).
set_bit_1([T0|Ts], Bt0) ->
Type = type_to_record(T0),
Bt = merge_bittype(Type, Bt0),
set_bit_1(Ts, Bt);
set_bit_1([], Bt) -> Bt.
type_to_record(integer) -> #bittype{type = integer};
type_to_record(utf8) -> #bittype{type = utf8};
type_to_record(utf16) -> #bittype{type = utf16};
type_to_record(utf32) -> #bittype{type = utf32};
type_to_record(float) -> #bittype{type = float};
type_to_record(binary) -> #bittype{type = binary};
type_to_record(bytes) -> #bittype{type = binary, unit = 8};
type_to_record(bitstring) -> #bittype{type = binary, unit = 1};
type_to_record(bits) -> #bittype{type = binary, unit = 1};
type_to_record({unit,undefined}) ->
#bittype{unit=undefined};
type_to_record({unit,Sz}) when is_integer(Sz), Sz > 0, Sz =< 256 ->
#bittype{unit=Sz};
type_to_record(big) -> #bittype{endian = big};
type_to_record(little) -> #bittype{endian = little};
type_to_record(native) -> #bittype{endian = native};
type_to_record(signed) -> #bittype{sign = signed};
type_to_record(unsigned) -> #bittype{sign = unsigned};
type_to_record(Name) -> throw({error,{undefined_bittype,Name}}).
%%
%% Merge two bit type specifications.
%%
merge_bittype(B1, B2) ->
Endian = merge_field(B1#bittype.endian, B2#bittype.endian, endianness),
Sign = merge_field(B1#bittype.sign, B2#bittype.sign, sign),
Type = merge_field(B1#bittype.type, B2#bittype.type, type),
Unit = merge_field(B1#bittype.unit, B2#bittype.unit, unit),
#bittype{type=Type,unit=Unit,endian=Endian,sign=Sign}.
merge_field(undefined, B, _) -> B;
merge_field(A, undefined, _) -> A;
merge_field(A, A, _) -> A;
merge_field(X, Y, What) ->
throw({error,{bittype_mismatch,X,Y,atom_to_list(What)}}).
%%
%% Defaults are as follows.
%%
%% The default is integer.
%% The default size is 'all' for binaries, 8 for integers, 64 for floats.
%% No unit must be given if the size is not given.
%% The default unit size is 8 for binaries, and 1 for integers and floats.
%% The default sign is always unsigned.
%% The default endian is always big.
%%
apply_defaults(undefined, Size, Unit, Sign, Endian) -> %default type
apply_defaults(integer, Size, Unit, Sign, Endian);
apply_defaults(binary, default, Unit, Sign, Endian) -> %default size
%% check_unit(Unit), removed to allow bitlevel binaries
apply_defaults(binary, all, Unit, Sign, Endian);
apply_defaults(integer, default, Unit, Sign, Endian) ->
check_unit(Unit),
apply_defaults(integer, 8, 1, Sign, Endian);
apply_defaults(utf8=Type, default, Unit, Sign, Endian) ->
apply_defaults(Type, undefined, Unit, Sign, Endian);
apply_defaults(utf16=Type, default, Unit, Sign, Endian) ->
apply_defaults(Type, undefined, Unit, Sign, Endian);
apply_defaults(utf32=Type, default, Unit, Sign, Endian) ->
apply_defaults(Type, undefined, Unit, Sign, Endian);
apply_defaults(float, default, Unit, Sign, Endian) ->
check_unit(Unit),
apply_defaults(float, 64, 1, Sign, Endian);
apply_defaults(binary, Size, undefined, Sign, Endian) -> %default unit
apply_defaults(binary, Size, 8, Sign, Endian);
apply_defaults(integer, Size, undefined, Sign, Endian) ->
apply_defaults(integer, Size, 1, Sign, Endian);
apply_defaults(float, Size, undefined, Sign, Endian) ->
apply_defaults(float, Size, 1, Sign, Endian);
apply_defaults(Type, Size, Unit, undefined, Endian) -> %default sign
apply_defaults(Type, Size, Unit, unsigned, Endian);
apply_defaults(Type, Size, Unit, Sign, undefined) -> %default endian
apply_defaults(Type, Size, Unit, Sign, big);
apply_defaults(Type, Size, Unit, Sign, Endian) -> %done
check_size_unit(Type, Size, Unit),
{ok,Size,#bittype{type=Type,unit=Unit,sign=Sign,endian=Endian}}.
check_size_unit(utf8, Size, Unit) ->
check_size_unit_1(Size, Unit);
check_size_unit(utf16, Size, Unit) ->
check_size_unit_1(Size, Unit);
check_size_unit(utf32, Size, Unit) ->
check_size_unit_1(Size, Unit);
check_size_unit(_, _, _) -> ok.
check_size_unit_1(Size, Unit) ->
case Size of
default -> ok;
undefined -> ok;
{atom,_,undefined} -> ok;
{value,_,undefined} -> ok;
_ -> throw({error,utf_bittype_size_or_unit})
end,
case Unit of
undefined -> ok;
_ -> throw({error,utf_bittype_size_or_unit})
end.
check_unit(undefined) -> ok;
check_unit(_) -> throw({error,bittype_unit}).