2

I just started my path on Erlang and I'm facing a problem I can't sort out a solution about:

I wrote a metod to take a domain espressed as a binary string, i.e. <<"www.404pagenotfound.com">> and convert it in the domain format as required for DNS protocol, so: <<3,"www",15,"pagenotfound",3,"com">>.

In the following the code (I rewroted it many times in different ways):

domainbyte(Bin) ->
    if byte_size(Bin) > 0 ->
        Res =  binary:split(Bin, <<".">>),
        [Chunk|[RestList]] = Res, 
        ChunkSize = byte_size(Chunk),   
        if length(RestList) > 0 -> 
            Rest = domainbyte(RestList),  %% <- Got "bad argument" here!
            <<ChunkSize/binary,Chunk,Rest>>;
        true ->
            <<ChunkSize/binary,Chunk>>
        end
    end.

Thx in advance for any clues.

PS.

Thx to comments I've found the error in the code abobe:

if length(RestList) > 0 -> %% here RestList is binary data so length throw "bad argument" error.

I've rewroted the method this way, but still with no luck:

**NOTE: I was able to fix the code in the following, problem is that if you have a binary chunk and you want to use it in another binary string, you must specify /binary on it: something not obvious to me.

I.e.: consider this small code snip:

**

TT = <<"com">>, SS = <<3, TT, 0>> %% <- you get error: bad argument

** must be fixed this way: **

TT = <<"com">>, SS = <<3, TT/binary, 0>>

domainbyte(Bin) ->
    if byte_size(Bin) > 0 ->
        Res =  binary:split(Bin, <<".">>),
                if length(Res) > 1 -> 
                    [Chunk|[RestList]] = Res, 
                    ChunkSize = byte_size(Chunk),
                    Rest = domainbyte(RestList),
                    <<ChunkSize,Chunk,Rest>>;
                true -> 
                    [Chunk] = Res,
                    ChunkSize = byte_size(Chunk), 
                    <<ChunkSize,Chunk>>
                end
    end.

MdP

4
  • please don't hide your question in comments in the code... Commented Oct 16, 2012 at 15:58
  • 1
    Shouldn't the output from <<"www.404pagenotfound.com">> be <<3,"www",15,"404pagenotfound",3,"com">>? Commented Oct 16, 2012 at 20:07
  • Thx @rvirding, yes, it was a type, I corrected the original post. Commented Oct 17, 2012 at 6:29
  • @user1750572 Yes, you are right but the code as it stands will still crash, because of just what you have written. Commented Oct 17, 2012 at 18:25

3 Answers 3

2

I think the easiest solution is to define the function using a binary comprehension:

domainbyte(Bin) ->
    Chunks = binary:split(Bin, <<".">>, [global]),      %A list of all the chunks
    << <<byte_size(C),C/binary>> || C <- Chunks >>.     %Build output binary

It might be slightly faster to build the output binary as a list of segments in a separate function then put them together in an iolist_to_binary/1. Note that if a '.' occurs outermost in the binary then this code will take that as an empty segment of length 0. If these should be discarded then you nee to add the option trim to binary:split/3. Note also that the size will occupy only one byte.

@Alnitak has the separate function but builds the binary one segment at time so it is not more efficient than the binary comprehension which does the same thing.

N.B. that if you have a binary segment Chunk/binary when constructing a binary this means that Chunk IS a binary, not that it should become one. Binaries are flat structures, think byte arrays, so everything becomes a binary. Or rather the binary.

EDIT: Though I see I missed the 0 which should be at the end. That is left as an exercise to the reader.

EDIT: Being in teaching mode, apart from the constructing binaries, a key to writing good Erlang code is understanding pattern matching. You use a bit but could do it more:

domainbyte(Bin) ->
    case binary:split(Bin, <<".">>) of
        [Chunk,Rest] ->                       %There was a '.'
            RestBin = domainbyte(Rest),
            Size = byte_size(Chunk),
            <<Size,Chunk/binary,RestBin/binary>>;
        [Chunk] ->                            %This was the last chunk
            Size = byte_size(Chunk),
            <<Size,Chunk/binary,0>>           %Add terminating 0
end.

This is basically doing the same as your code but we are using pattern matching to select a clause, not only to pull apart a known structure. Pattern matching is the basic method for control, not just in case as here but also in functions and receive. This results in if being used quite sparingly.

Enough from me for now.

Sign up to request clarification or add additional context in comments.

3 Comments

Thx a lot, as told, I'm just an erlang newcomer and I completely missed comprenhension feature.... anyway, what about my second implementation ? Altought bad, seems correct to me, so, why I get error ? I'm trying to learn and such kind of errors are frustrating... thx.
@user1750572 Comprehensions, list and binary, are a bit esoteric though in some cases they can lead to very concise and succinct code.
@user1750572 Just added a bit on an alternative way of writing your function.
1

I'm a bit rusty on Erlang, but I think your problem is that RestList is an array of chunks from the output of binary:split.

So when you chuck it recursively back into domainbyte it's in the wrong format.

Also - don't forget the terminating NUL byte to represent the root label!

FWIW, here's my working version:

label([]) ->
    << 0 >>;

label([H|T]) ->
    D = label(H),
    P = label(T),
    << D/binary, P/binary>>;

label(A) ->
    L = byte_size(A),
    << <<L>>/binary, A/binary>>.

domainbyte(A) ->
    Res = binary:split(A, <<".">>, [global, trim]),
    label(Res).

It correctly adds a trailing NUL byte, and trims any extra trailing dots.

7 Comments

Hi, thx for the reply, but as you can see into the code, I already considered that:
As you can see I considered it: [Chunk|RestList]] = Res and thx for the NUL byte advice...
Are you sure about that additional set of [] around RestList? Without the global option the RestList should just be the remaining parent domain label. I'm trying to replicate this but my installed Erlang doesn't have the binary module.
hi @Alnitak, yes it is ok, in this source the problem is related to the length method used on binary data at row 6: if length(RestList) > 0. I've modified the code as in the following with another nasty prob. :( domainbyte(Bin) -> if byte_size(Bin) > 0 -> Res = binary:split(Bin, <<".">>), if length(Res) > 1 -> [Chunk|[RestList]] = Res, ChunkSize = byte_size(Chunk), Rest = domainbyte(RestList), <<ChunkSize/binary,Chunk,Rest>>; true -> [Chunk] = Res, ChunkSize = byte_size(Chunk), <<ChunkSize/binary,Chunk>> end end.
You shouldn't really have the label/1 function work on both the list of chunks as well as on each chunk. Though it would allow you to have nested lists of binaries and lists.
|
0

The concat of binaries should be written like this:

<< <<ChunkSize>>/binary, Chunk/binary, Rest/binary>>
% and
<< <<ChunkSize>>/binary, Chunk/binary>>

1 Comment

Thx @halfelf, just figured out ... :D

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.