← Back to list

Битовые сдвиги в Erlang

Для перелезших с Сишечки посвящается.

m2k · 2015-09-07 00:45 · 2 claps · 1.7 min read
#erlang #binary #mnesia
Open on Medium ↗

Битовые сдвиги в Erlang

Для перелезших с Сишечки посвящается.

Последнее время я часто сижу по веществами…

…и захотел я 150-метровую базу сдампить в файл. База с данными stackoverflow, один чел уже загнал её во мнезию, хотел свою такую-же только не в убогом исполнении:

Решил я по старинке наговнокодить:

t_write() ->
  {ok,IoDevice}=file:open("file.bin",[write,raw,binary]),
  file:write(IoDevice,<<"sasay\n">>),
  file:write(IoDevice,<<"lalka">>),
  ok=file:close(IoDevice).
t_read() ->
  {ok,IoDevice}=file:open("file.bin",[read,raw,binary]),
  Foreach=fun ReadLine(Fun) ->
    case file:read_line(IoDevice) of
      eof -> ok=file:close(IoDevice);
      {error, _Reason} -> ok=file:close(IoDevice);
      {ok, Data} -> Fun(Data), ReadLine(Fun)
    end end,
  Foreach(fun(Data) -> io:format("Part: ~p~n",[Data]) end).

И результат после сохранения и восстановления базы:

main:t_db_restore().
** exception error: bad argument
in function binary_to_term/1
called as binary_to_term([131,104,13,100,0,9,111,112,101,114,97,116,105,111,110,97,5,100,0,9,117,110,100,101,102,105,110,101|…])

В общем, такие дела, посоны, что вы не должны хотеть писать данные в файл построчно :) Либо файл целиком file:write_file/3, либо чанками file:write/2. Никаких сепараторов. Логика парсинга должна быть вынесена из уровня стандартной библиотеки на уровень приложения.

Так как записи таблицы мы кодируем в рассово-верный BERT, надо определиться с разделителями. Допустим, ими будет смещение до следующего рекорда.

<<9>> <<131,104,1,100,0,3,120,117,121>>

Смещение должно быть фиксированного размера, и например, могло описать число 4'294'967'295 — кул, уложимся в 32 бита, классика.

Надо было нарезать длину упакованого в BERT рекорда на 4 байта.

Тут я зачем-то вспомнил Си с битовыми сдвигами, и как в нём всё быстро — и уехал в астрал.

Дело в том, что в Эрланге битовый сдвиг влево — арифметический, то есть биты сдвигаясь влево не теряются, ведь тип Integer не ограничен в размере. Поэтому сегодня без битовых сдвигов, бро.

Решение простое:

<<4294967295:32>>. => <<255,255,255,255>>
<<3672:32>>. => <<0,0,14,88>>

Идеально. Обратно тоже работает. ВНЕЗАПНО

<<N:32>> = <<1,0,1,0>>.
N. => 16777472

Теперь надо переписать построчные говнофункции.

t_db_store2() ->
  mnesia:transaction(fun()->
    {ok,IoDevice}=file:open("table.dat",[write,raw,binary]),
    mnesia:foldl(fun(Record,Acc) ->
      Bin=term_to_binary(Record),
      file:write(IoDevice,<<(size(Bin)):32,Bin/binary>>),
      Acc
      end,undefined,TABLE_NAME),
    ok=file:close(IoDevice) end).
t_db_restore2() ->
  {ok,IoDevice}=file:open("table.dat",[read,raw,binary]),
  Foreach=fun ReadBlock(Fun) when is_function(Fun) ->
        ReadBlock({header,4,Fun});
      ReadBlock({Type,Count,Fun}) ->
        case file:read(IoDevice,Count) of
          eof -> ok=file:close(IoDevice);
          {error, _Reason} -> ok=file:close(IoDevice);
          {ok, <<BlockSize:32>>} when Type =:= header ->
            ReadBlock({data,BlockSize,Fun});
          {ok, Data} when Type =:= data ->
            Fun(Data),
            ReadBlock({header,4,Fun})
        end
  end,
  Foreach(fun(Data) ->
    io:format("Part: ~p~n",[binary_to_term(Data)]) end).

Ладно. Раз уж статья про битовые операции, нате вам хотя бы замену erlang:now() который deprecated начиная с R18

integer_to_list(unique_integer() rem 1000000).

메타데이터
post_id
ddcc4f857599
slug
битовые-сдвиги-в-erlang-ddcc4f857599
url
https://medium.com/@m2k/%D0%B1%D0%B8%D1%82%D0%BE%D0%B2%D1%8B%D0%B5-%D1%81%D0%B4%D0%B2%D0%B8%D0%B3%D0%B8-%D0%B2-erlang-ddcc4f857599
canonical_url
https://medium.com/@m2k/%D0%B1%D0%B8%D1%82%D0%BE%D0%B2%D1%8B%D0%B5-%D1%81%D0%B4%D0%B2%D0%B8%D0%B3%D0%B8-%D0%B2-erlang-ddcc4f857599
author_url
https://medium.com/@m2k
status
ok
fetched_at
2026-08-27 01:51:41