zf

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

array.ex (3517B)


      1 defmodule Postgrex.Extensions.Array do
      2   @moduledoc false
      3   import Postgrex.BinaryUtils, warn: false
      4   @behaviour Postgrex.SuperExtension
      5 
      6   def init(_), do: nil
      7 
      8   def matching(_),
      9     do: [send: "array_send"]
     10 
     11   def format(_),
     12     do: :super_binary
     13 
     14   def oids(%Postgrex.TypeInfo{array_elem: elem_oid}, _),
     15     do: [elem_oid]
     16 
     17   def encode(_) do
     18     quote location: :keep do
     19       list, [oid], [type] when is_list(list) ->
     20         # encode_list/2 defined by TypeModule
     21         encoder = &encode_list(&1, type)
     22         unquote(__MODULE__).encode(list, oid, encoder)
     23 
     24       other, _, _ ->
     25         raise DBConnection.EncodeError, Postgrex.Utils.encode_msg(other, "a list")
     26     end
     27   end
     28 
     29   def decode(_) do
     30     quote location: :keep do
     31       <<len::int32(), binary::binary-size(len)>>, [oid], [type] ->
     32         <<ndims::int32(), _has_null::int32(), ^oid::uint32(), dims::size(ndims)-binary-unit(64),
     33           data::binary>> = binary
     34 
     35         # decode_list/2 defined by TypeModule
     36         flat = decode_list(data, type)
     37 
     38         unquote(__MODULE__).decode(dims, flat)
     39     end
     40   end
     41 
     42   ## Helpers
     43 
     44   # Special case for empty lists. This treats an empty list as an empty 1-dim array.
     45   # While libpq will decode an payload encoded for a 0-dim array, CockroachDB will not.
     46   # Also, this is how libpq actually encodes 0-dim arrays.
     47   def encode([], elem_oid, _encoder) do
     48     <<20::int32(), 1::int32(), 0::int32(), elem_oid::uint32(), 0::int32(), 1::int32()>>
     49   end
     50 
     51   def encode(list, elem_oid, encoder) do
     52     {data, ndims, lengths} = encode(list, 0, [], encoder)
     53     lengths = for len <- Enum.reverse(lengths), do: <<len::int32(), 1::int32()>>
     54     iodata = [<<ndims::int32(), 0::int32(), elem_oid::uint32()>>, lengths, data]
     55     [<<IO.iodata_length(iodata)::int32()>> | iodata]
     56   end
     57 
     58   defp encode([], ndims, lengths, _encoder) do
     59     {"", ndims, lengths}
     60   end
     61 
     62   defp encode([head | tail] = list, ndims, lengths, encoder) when is_list(head) do
     63     lengths = [length(list) | lengths]
     64     {data, ndims, lengths} = encode(head, ndims, lengths, encoder)
     65     [dimlength | _] = lengths
     66 
     67     rest =
     68       Enum.reduce(tail, [], fn sublist, acc ->
     69         {data, _, [len | _]} = encode(sublist, ndims, lengths, encoder)
     70 
     71         if len != dimlength do
     72           raise ArgumentError, "nested lists must have lists with matching lengths"
     73         end
     74 
     75         [acc | data]
     76       end)
     77 
     78     {[data | rest], ndims + 1, lengths}
     79   end
     80 
     81   defp encode(list, ndims, lengths, encoder) do
     82     {encoder.(list), ndims + 1, [length(list) | lengths]}
     83   end
     84 
     85   def decode(dims, elems) do
     86     case decode_dims(dims, []) do
     87       [] when elems == [] ->
     88         []
     89 
     90       [length] when length(elems) == length ->
     91         Enum.reverse(elems)
     92 
     93       lengths ->
     94         {array, []} = nest(elems, lengths)
     95         array
     96     end
     97   end
     98 
     99   defp decode_dims(<<len::int32(), _lbound::int32(), rest::binary>>, acc) do
    100     decode_dims(rest, [len | acc])
    101   end
    102 
    103   defp decode_dims(<<>>, acc) do
    104     Enum.reverse(acc)
    105   end
    106 
    107   # elems and lengths in reverse order
    108   defp nest(elems, [len]) do
    109     nest_inner(elems, len, [])
    110   end
    111 
    112   defp nest(elems, [len | lengths]) do
    113     nest(elems, len, lengths, [])
    114   end
    115 
    116   defp nest(elems, 0, _, acc) do
    117     {acc, elems}
    118   end
    119 
    120   defp nest(elems, n, lengths, acc) do
    121     {row, elems} = nest(elems, lengths)
    122     nest(elems, n - 1, lengths, [row | acc])
    123   end
    124 
    125   defp nest_inner(elems, 0, acc) do
    126     {acc, elems}
    127   end
    128 
    129   defp nest_inner([elem | elems], n, acc) do
    130     nest_inner(elems, n - 1, [elem | acc])
    131   end
    132 end