meta-google: network-sh: Add ip_bytes_to_str

This makes it possible to get a human readable address back from a byte
array.

Change-Id: Ifcc98bcc95b8d75fe7d1aae1c264cbddf3fc5bd0
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/meta-google/recipes-google/networking/network-sh/lib.sh b/meta-google/recipes-google/networking/network-sh/lib.sh
index 2dc5103..04e1480 100644
--- a/meta-google/recipes-google/networking/network-sh/lib.sh
+++ b/meta-google/recipes-google/networking/network-sh/lib.sh
@@ -153,6 +153,60 @@
   bytes_out=("${bytes[@]}")
 }
 
+ip_bytes_to_str() {
+  local -n bytes="$1"
+
+  if (( "${#bytes[@]}" == 4 )); then
+    printf '%d.%d.%d.%d\n' "${bytes[@]}"
+  elif (( "${#bytes[@]}" == 16 )); then
+    # Track the starting position of the longest run of 0 hextets (2 bytes)
+    local longest_i=0
+    # Track the size of the longest run of 0 hextets
+    local longest_s=0
+    # The index of the first 0 byte in the current run of zeros
+    local first_zero=0
+    local i
+    # Find the location of the longest run of zero hextets, preferring same
+    # size runs later in the address.
+    for (( i=0; i<=16; i+=2 )); do
+      # Terminate the run of zeros if we are at the end of the array or
+      # have a non-zero hextet
+      if (( i == 16 || bytes[$i] != 0 || bytes[$((i+1))] != 0 )); then
+        local s=$((i - first_zero))
+        if (( s >= longest_s )); then
+          longest_i=$first_zero
+          longest_s=$s
+        fi
+        first_zero=$((i+2))
+      fi
+    done
+    # Build the address string by each hextet
+    for (( i=0; i<16; i+=2 )); do
+      # If we encountered a run of zeros, add the necessary :: at the end
+      # of the string. If not at the end, a single : is added since : is
+      # printed to subsequent hextets already.
+      if (( i == longest_i )); then
+        (( i += longest_s-2 ))
+        printf ':'
+        # End of string needs to be ::
+        if (( i == 14 )); then
+          printf ':'
+        fi
+      else
+        # Prepend : to all hextets except the first for separation
+        if (( i != 0 )); then
+          printf ':'
+        fi
+        printf '%x' $(( (bytes[$i]<<8) | bytes[$(($i+1))]))
+      fi
+    done
+    printf '\n'
+  else
+    echo "Invalid IP Bytes: ${bytes[*]}" >&2
+    return 1
+  fi
+}
+
 ipv6_pfx_concat() {
   local pfx="$1"
   local sfx="$2"
diff --git a/meta-google/recipes-google/networking/network-sh/test.sh b/meta-google/recipes-google/networking/network-sh/test.sh
index 2361cfe..33cab86 100755
--- a/meta-google/recipes-google/networking/network-sh/test.sh
+++ b/meta-google/recipes-google/networking/network-sh/test.sh
@@ -114,6 +114,36 @@
   out=()
 }
 
+test_ip4_bytes_str() {
+  in=(10 0 255 1)
+  str="$(ip_bytes_to_str in)" || fail
+  expect_streq "$str" '10.0.255.1'
+}
+
+test_ip6_bytes_str() {
+  in=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
+  str="$(ip_bytes_to_str in)" || fail
+  expect_streq "$str" '::'
+  in=(0xfd 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
+  str="$(ip_bytes_to_str in)" || fail
+  expect_streq "$str" 'fd00::'
+  in=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0xfd)
+  str="$(ip_bytes_to_str in)" || fail
+  expect_streq "$str" '::fd'
+  in=(0xfd 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1)
+  str="$(ip_bytes_to_str in)" || fail
+  expect_streq "$str" 'fd01::1'
+  in=(0xfd 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1)
+  str="$(ip_bytes_to_str in)" || fail
+  expect_streq "$str" 'fd01::1:0:0:1'
+  in=(0xfd 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1)
+  str="$(ip_bytes_to_str in)" || fail
+  expect_streq "$str" 'fd01:0:0:1:1::1'
+  in=(0 1 0 1 0xdd 0xdd 0 1 0 1 0 1 0 1 0 1)
+  str="$(ip_bytes_to_str in)" || fail
+  expect_streq "$str" '1:1:dddd:1:1:1:1:1'
+}
+
 test_ipv6_pfx_concat() {
   # Invalid inputs
   expect_err 1 ipv6_pfx_concat 'fd/64' '1234:5678:90af'