zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

numeric.ex (4217B)


      1 defmodule Postgrex.Extensions.Numeric do
      2   @moduledoc false
      3   import Postgrex.BinaryUtils, warn: false
      4   use Postgrex.BinaryExtension, send: "numeric_send"
      5 
      6   def encode(_) do
      7     quote location: :keep, generated: true do
      8       %Decimal{} = decimal ->
      9         data = unquote(__MODULE__).encode_numeric(decimal)
     10         [<<IO.iodata_length(data)::int32()>> | data]
     11 
     12       n when is_float(n) ->
     13         data = unquote(__MODULE__).encode_numeric(Decimal.from_float(n))
     14         [<<IO.iodata_length(data)::int32()>> | data]
     15 
     16       n when is_integer(n) ->
     17         data = unquote(__MODULE__).encode_numeric(Decimal.new(n))
     18         [<<IO.iodata_length(data)::int32()>> | data]
     19     end
     20   end
     21 
     22   def decode(_) do
     23     quote location: :keep do
     24       <<len::int32(), data::binary-size(len)>> ->
     25         unquote(__MODULE__).decode_numeric(data)
     26     end
     27   end
     28 
     29   ## Helpers
     30 
     31   # TODO: remove qNaN and sNaN when we depend on Decimal 2.0
     32   def encode_numeric(%Decimal{coef: coef}) when coef in [:NaN, :qNaN, :sNaN] do
     33     <<0::int16(), 0::int16(), 0xC000::uint16(), 0::int16()>>
     34   end
     35 
     36   def encode_numeric(%Decimal{sign: 1, coef: :inf}) do
     37     <<0::int16(), 0::int16(), 0xD000::uint16(), 0::int16()>>
     38   end
     39 
     40   def encode_numeric(%Decimal{sign: -1, coef: :inf}) do
     41     <<0::int16(), 0::int16(), 0xF000::uint16(), 0::int16()>>
     42   end
     43 
     44   def encode_numeric(%Decimal{sign: sign, coef: coef, exp: exp}) do
     45     sign = encode_sign(sign)
     46     scale = -exp
     47 
     48     {integer, float, scale} = split_parts(coef, scale)
     49     integer_digits = encode_digits(integer, [])
     50     float_digits = encode_float(float, scale)
     51     digits = integer_digits ++ float_digits
     52 
     53     num_digits = length(digits)
     54     weight = max(length(integer_digits) - 1, 0)
     55 
     56     bin = for digit <- digits, into: "", do: <<digit::uint16()>>
     57     [<<num_digits::int16(), weight::int16(), sign::uint16(), scale::int16()>> | bin]
     58   end
     59 
     60   defp encode_sign(1), do: 0x0000
     61   defp encode_sign(-1), do: 0x4000
     62 
     63   defp split_parts(coef, scale) when scale >= 0 do
     64     integer_base = pow10(scale)
     65     {div(coef, integer_base), rem(coef, integer_base), scale}
     66   end
     67 
     68   defp split_parts(coef, scale) when scale < 0 do
     69     integer_base = pow10(-scale)
     70     {coef * integer_base, 0, 0}
     71   end
     72 
     73   defp encode_float(float, scale) do
     74     pending = pending_scale(float, scale)
     75     float_prefix = div(pending, 4)
     76     float_suffix = 4 - rem(scale, 4)
     77     float = float * pow10(float_suffix)
     78     List.duplicate(0, float_prefix) ++ encode_digits(float, [])
     79   end
     80 
     81   defp pending_scale(0, scale), do: scale
     82   defp pending_scale(num, scale), do: pending_scale(div(num, 10), scale - 1)
     83 
     84   defp encode_digits(coef, digits) when coef < 10_000 do
     85     [coef | digits]
     86   end
     87 
     88   defp encode_digits(coef, digits) do
     89     digit = rem(coef, 10_000)
     90     coef = div(coef, 10_000)
     91     encode_digits(coef, [digit | digits])
     92   end
     93 
     94   def decode_numeric(
     95         <<ndigits::int16(), weight::int16(), sign::uint16(), scale::int16(), tail::binary>>
     96       ) do
     97     decode_numeric(ndigits, weight, sign, scale, tail)
     98   end
     99 
    100   @nan Decimal.new("NaN")
    101   @positive_inf Decimal.new("Inf")
    102   @negative_inf Decimal.new("-Inf")
    103 
    104   defp decode_numeric(0, _weight, 0xC000, _scale, "") do
    105     @nan
    106   end
    107 
    108   defp decode_numeric(0, _weight, 0xD000, _scale, "") do
    109     @positive_inf
    110   end
    111 
    112   defp decode_numeric(0, _weight, 0xF000, _scale, "") do
    113     @negative_inf
    114   end
    115 
    116   defp decode_numeric(_num_digits, weight, sign, scale, bin) do
    117     {value, weight} = decode_numeric_int(bin, weight, 0)
    118     sign = decode_sign(sign)
    119     coef = scale(value, (weight + 1) * 4 + scale)
    120     Decimal.new(sign, coef, -scale)
    121   end
    122 
    123   defp decode_sign(0x0000), do: 1
    124   defp decode_sign(0x4000), do: -1
    125 
    126   defp scale(coef, 0), do: coef
    127   defp scale(coef, diff) when diff < 0, do: div(coef, pow10(-diff))
    128   defp scale(coef, diff) when diff > 0, do: coef * pow10(diff)
    129 
    130   Enum.reduce(0..100, 1, fn x, acc ->
    131     defp pow10(unquote(x)), do: unquote(acc)
    132     acc * 10
    133   end)
    134 
    135   defp pow10(num) when num > 100, do: pow10(100) * pow10(num - 100)
    136 
    137   defp decode_numeric_int("", weight, acc), do: {acc, weight}
    138 
    139   defp decode_numeric_int(<<digit::int16(), tail::binary>>, weight, acc) do
    140     acc = acc * 10_000 + digit
    141     decode_numeric_int(tail, weight - 1, acc)
    142   end
    143 end