%% Copyright (c) 2011-2023 Robert Virding %% %% 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. %%% File : lfe_bits.erl %%% Author : Robert Virding %%% Purpose : Lisp Flavoured Erlang common functions for binaries. -module(lfe_bits). -export([parse_bitspecs/1,get_bitspecs/1]). %% The standard imports -import(lists, [foldl/3]). %% Everything default'ed. -record(spec, {type=default,size=default,unit=default, sign=default,endian=default}). %% get_bitspecs(Specs) -> {ok,Size,{Type,Unit,Sign,End}} | {error,Error}. %% Parse a bitspec, apply defaults and return data. The size field is %% unevaluated. We only return the first error found. get_bitspecs(Specs) -> try #spec{type=Ty0,size=Sz0,unit=Un0,sign=Si0,endian=En0} = parse_bitspecs(Specs, #spec{}), {Ty,Sz,Un,Si,En} = apply_defaults(Ty0, Sz0, Un0, Si0, En0), {ok,Sz,{Ty,Un,Si,En}} catch throw:Error -> Error end. %% parse_bitspecs(Specs) -> {ok,Size,{Type,Unit,Sign,End}} | {error,Error}. %% Parse a bitspec and return data. Unmentioned fields get the value %% default. We only return the first error found. parse_bitspecs(Specs) -> case catch parse_bitspecs(Specs, #spec{}) of #spec{type=Ty,size=Sz,unit=Un,sign=Si,endian=En} -> {ok,Sz,{Ty,Un,Si,En}}; Error -> Error end. %% parse_bitspecs(Specs, #spec{}) -> #spec{}. %% Parse a bitspec and return a #spec{} record. Unmentioned fields %% get the value default. Errors throw the tuple {error,Error} and %% must be caught. parse_bitspecs(Ss, Sp0) -> foldl(fun (S, Sp) -> parse_bitspec(S, Sp) end, Sp0, Ss). %% parse_bitspec(Spec, #spec{}) -> #spec{}. %% We also convert synonyms to the standard value. %% Types. parse_bitspec(integer, Sp) -> Sp#spec{type=integer}; parse_bitspec(float, Sp) -> Sp#spec{type=float}; parse_bitspec(binary, Sp) -> Sp#spec{type=binary,unit=8}; parse_bitspec(bytes, Sp) -> Sp#spec{type=binary,unit=8}; parse_bitspec(bitstring, Sp) -> Sp#spec{type=binary,unit=1}; parse_bitspec(bits, Sp) -> Sp#spec{type=binary,unit=1}; %% Unicode types. parse_bitspec('utf8', Sp) -> Sp#spec{type=utf8}; parse_bitspec('utf-8', Sp) -> Sp#spec{type=utf8}; parse_bitspec('utf16', Sp) -> Sp#spec{type=utf16}; parse_bitspec('utf-16', Sp) -> Sp#spec{type=utf16}; parse_bitspec('utf32', Sp) -> Sp#spec{type=utf32}; parse_bitspec('utf-32', Sp) -> Sp#spec{type=utf32}; %% Endianness. parse_bitspec('big', Sp) -> Sp#spec{endian=big}; parse_bitspec('big-endian', Sp) -> Sp#spec{endian=big}; parse_bitspec('little', Sp) -> Sp#spec{endian=little}; parse_bitspec('little-endian', Sp) -> Sp#spec{endian=little}; parse_bitspec('native', Sp) -> Sp#spec{endian=native}; parse_bitspec('native-endian', Sp) -> Sp#spec{endian=native}; %% Sign. parse_bitspec(signed, Sp) -> Sp#spec{sign=signed}; parse_bitspec(unsigned, Sp) -> Sp#spec{sign=unsigned}; %% Size and unit, return these as is. parse_bitspec([size,S], Sp) -> Sp#spec{size=S}; parse_bitspec([unit,U], Sp) when is_integer(U), U > 0, U =< 256 -> Sp#spec{unit=U}; parse_bitspec(Spec, _) -> throw({error,{undefined_bittype,Spec}}). %% apply_defaults(Type, Size, Unit, Sign, Endian) -> %% {Type,Size,Unit,Sign,Endian}. %% This is taken almost directly from erl_bits.erl. %% Default type. apply_defaults(default, Sz, Un, Si, En) -> apply_defaults(integer, Sz, Un, Si, En); %% Default size. apply_defaults(binary, default, Un, Si, En) -> apply_defaults(binary, all, Un, Si, En); apply_defaults(integer, default, Un, Si, En) -> check_unit(Un), apply_defaults(integer, 8, Un, Si, En); apply_defaults(utf8, default, Un, Si, En) -> apply_defaults(utf8, undefined, Un, Si, En); apply_defaults(utf16, default, Un, Si, En) -> apply_defaults(utf16, undefined, Un, Si, En); apply_defaults(utf32, default, Un, Si, En) -> apply_defaults(utf32, undefined, Un, Si, En); apply_defaults(float, default, Un, Si, En) -> check_unit(Un), apply_defaults(float, 64, 1, Si, En); %% Default unit. apply_defaults(binary, Sz, default, Si, En) -> apply_defaults(binary, Sz, 8, Si, En); apply_defaults(integer, Sz, default, Si, En) -> apply_defaults(integer, Sz, 1, Si, En); apply_defaults(float, Sz, default, Si, En) -> apply_defaults(float, Sz, 1, Si, En); %% Default sign. apply_defaults(Ty, Sz, Un, default, En) -> apply_defaults(Ty, Sz, Un, unsigned, En); %% Default endian. apply_defaults(Ty, Sz, Un, Si, default) -> apply_defaults(Ty, Sz, Un, Si, big); %% Done. apply_defaults(Ty, Sz, Un, Si, En) -> {Ty,Sz,Un,Si,En}. check_unit(default) -> ok; check_unit(_) -> throw({error,bittype_unit}).