3
\$\begingroup\$

I'm very new to Go, and am trying to get some experience by re-writing some of my Python code in Go. Below is a function that takes an IP address (ipv4) in integer form and returns the string version. From 572779703 to 34.35.236.183 for example.

func int2ip(num int) (string) {
    result   := [4]int{}
    for i, n := range []uint{24, 16, 8, 0} {
        result[i] = (num >> n) & 255
    }
    return fmt.Sprintf("%d.%d.%d.%d", result[0], result[1], result[2], result[3])
}

I'm not only curious if there is a more straightforward or concise way to do this with Go, but am very curious if there is a way to use the []result array in Sprintf() without having to type out every index.

Something similar to:

fmt.Sprintf("%d.%d.%d.%d", result)
\$\endgroup\$
2
\$\begingroup\$

The proper way of doing this is to use the tools the language already gave you.

IPv4 usually are stored as big endian numbers, and Go already have an IP type:

func int2ip(num int) net.IP {
    var b [4]byte
    binary.BigEndian.PutUint32(b[:], uint32(num))
    return net.IPv4(b[0], b[1], b[2], b[3])
}

//or if you don't care about returning net.IP
func int2ipstr(num int) string {
    var b [4]byte
    binary.BigEndian.PutUint32(b[:], uint32(num))
    return fmt.Sprintf("%d.%d.%d.%d", b[0], b[1], b[2], b[3])
}

playground

//edit adding more details following @Schism's advice

Using Sprintf is usually fine and they worked on optimizing it fairly well, if you want to squeeze every drop of performance out of it, you could use a more "buffered" alternative like :

func int2ipb(num int) string {
    b, buf := [4]byte{}, make([]byte, 0, 15) //15 = max number of bytes in an ip
    binary.BigEndian.PutUint32(b[:], uint32(num))
    for _, n := range b {
        buf = append(buf, '.')
        buf = append(buf, strconv.Itoa(int(n))[:]...)
    }
    return string(buf[1:])
}
\$\endgroup\$
  • \$\begingroup\$ Thats great! I did not think to look if Go already had an IP type. \$\endgroup\$ – tMC Aug 25 '14 at 16:08
0
\$\begingroup\$

Another way to do this would be to change result to a string slice and then use strings.Join.

I'm not super familiar with Go, but this works for me:

func int2ip(num int) (string) {
    var result = make([]string, 4)
    for i, n := range []uint{24, 16, 8, 0} {
        result[i] = strconv.Itoa((num >> n) & 255)
    }
    return strings.Join(result, ".")
}

Alternatively, leave the first line as var result [4]string, and change the last line to use result[:]. (This coerces the array into a slice.)

\$\endgroup\$
  • \$\begingroup\$ Creating 2 slices is just inefficient, specially a string slice. \$\endgroup\$ – OneOfOne Aug 23 '14 at 3:25
  • 1
    \$\begingroup\$ @OneOfOne I appreciate your input; as mentioned, I'm not familiar with Go. However, I note that your answer specifically does not address the part that the OP was "very curious" about. Could you please add an explanation in your answer regarding why it's better to leave in the Sprintf? \$\endgroup\$ – Schism Aug 23 '14 at 21:20
  • \$\begingroup\$ Mainly because of []string there's more copying and moving things around, I'd need to benchmark it though, your method might be good. \$\endgroup\$ – OneOfOne Aug 23 '14 at 22:02
  • \$\begingroup\$ @OneOfOne No, I trust yours is probably better; I expect that checkmark to move to yours. I was just suggesting you add that discussion to your answer to make it even better :-) \$\endgroup\$ – Schism Aug 24 '14 at 0:15
  • \$\begingroup\$ I updated it, I'm terrible at explaining stuff :| \$\endgroup\$ – OneOfOne Aug 24 '14 at 0:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.