Commit 32ac1b5927

Vexu <15308111+Vexu@users.noreply.github.com>
2019-11-04 17:59:14
improve ipv6 parsing and formatting
1 parent 788848e
Changed files (2)
lib
lib/std/net/test.zig
@@ -4,10 +4,38 @@ const mem = std.mem;
 const testing = std.testing;
 
 test "parse and render IPv6 addresses" {
-    const addr = try net.IpAddress.parseIp6("FF01:0:0:0:0:0:0:FB", 80);
-    var buf: [100]u8 = undefined;
-    const printed = try std.fmt.bufPrint(&buf, "{}", addr);
-    std.testing.expect(mem.eql(u8, "[ff01::fb]:80", printed));
+    var buffer: [100]u8 = undefined;
+    const ips = [_][]const u8{
+        "FF01:0:0:0:0:0:0:FB",
+        "FF01::Fb",
+        "::1",
+        "::",
+        "2001:db8::",
+        "::1234:5678",
+        "2001:db8::1234:5678",
+        "FF01::FB%1234",
+    };
+    const printed = [_][]const u8{
+        "ff01::fb",
+        "ff01::fb",
+        "::1",
+        "::",
+        "2001:db8::",
+        "::1234:5678",
+        "2001:db8::1234:5678",
+        "ff01::fb"
+    };
+    for (ips) |ip, i| {
+        var addr = net.IpAddress.parseIp6(ip, 0) catch unreachable;
+        var newIp = std.fmt.bufPrint(buffer[0..], "{}", addr) catch unreachable;
+        std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3]));
+    }
+
+    testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp6(":::", 0));
+    testing.expectError(error.Overflow, net.IpAddress.parseIp6("FF001::FB", 0));
+    testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp6("FF01::Fb:zig", 0));
+    testing.expectError(error.InvalidEnd, net.IpAddress.parseIp6("FF01:0:0:0:0:0:0:FB:", 0));
+    testing.expectError(error.Incomplete, net.IpAddress.parseIp6("FF01:", 0));
 }
 
 test "parse and render IPv4 addresses" {
@@ -19,7 +47,7 @@ test "parse and render IPv4 addresses" {
         "123.255.0.91",
         "127.0.0.1",
     }) |ip| {
-        var addr = net.IpAddress.parseIp4(ip, 0);
+        var addr = net.IpAddress.parseIp4(ip, 0) catch unreachable;
         var newIp = std.fmt.bufPrint(buffer[0..], "{}", addr) catch unreachable;
         std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2]));
     }
lib/std/net.zig
@@ -50,19 +50,22 @@ pub const IpAddress = extern union {
     pub fn parseIp6(buf: []const u8, port: u16) !IpAddress {
         var result = IpAddress{
             .in6 = os.sockaddr_in6{
-                .scope_id = undefined,
+                .scope_id = 0,
                 .port = mem.nativeToBig(u16, port),
                 .flowinfo = 0,
                 .addr = undefined,
             },
         };
-        const ip_slice = result.in6.addr[0..];
+        var ip_slice = result.in6.addr[0..];
+
+        var tail: [16]u8 = undefined;
 
         var x: u16 = 0;
         var saw_any_digits = false;
         var index: u8 = 0;
         var scope_id = false;
-        for (buf) |c| {
+        var abbrv = false;
+        for (buf) |c, i| {
             if (scope_id) {
                 if (c >= '0' and c <= '9') {
                     const digit = c - '0';
@@ -77,7 +80,12 @@ pub const IpAddress = extern union {
                 }
             } else if (c == ':') {
                 if (!saw_any_digits) {
-                    return error.InvalidCharacter;
+                    if (abbrv) return error.InvalidCharacter; // ':::'
+                    if (i != 0) abbrv = true;
+                    mem.set(u8, ip_slice[index..], 0);
+                    ip_slice = tail[0..];
+                    index = 0;
+                    continue;
                 }
                 if (index == 14) {
                     return error.InvalidEnd;
@@ -93,12 +101,6 @@ pub const IpAddress = extern union {
                 if (!saw_any_digits) {
                     return error.InvalidCharacter;
                 }
-                if (index == 14) {
-                    ip_slice[index] = @truncate(u8, x >> 8);
-                    index += 1;
-                    ip_slice[index] = @truncate(u8, x);
-                    index += 1;
-                }
                 scope_id = true;
                 saw_any_digits = false;
             } else {
@@ -113,21 +115,22 @@ pub const IpAddress = extern union {
             }
         }
 
-        if (!saw_any_digits) {
+        if (!saw_any_digits and !abbrv) {
             return error.Incomplete;
         }
 
-        if (scope_id) {
-            return result;
-        }
-
         if (index == 14) {
             ip_slice[14] = @truncate(u8, x >> 8);
             ip_slice[15] = @truncate(u8, x);
             return result;
+        } else {
+            ip_slice[index] = @truncate(u8, x >> 8);
+            index += 1;
+            ip_slice[index] = @truncate(u8, x);
+            index += 1;
+            mem.copy(u8, result.in6.addr[16 - index ..], ip_slice[0..index]);
+            return result;
         }
-
-        return error.Incomplete;
     }
 
     pub fn parseIp4(buf: []const u8, port: u16) !IpAddress {
@@ -246,10 +249,6 @@ pub const IpAddress = extern union {
                 );
             },
             os.AF_INET6 => {
-                const ZeroRun = struct {
-                    index: usize,
-                    count: usize,
-                };
                 const port = mem.bigToNative(u16, self.in6.port);
                 const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.in6.addr);
                 const native_endian_parts = switch (builtin.endian) {
@@ -262,44 +261,21 @@ pub const IpAddress = extern union {
                         break :blk buf;
                     },
                 };
-
-                var longest_zero_run: ?ZeroRun = null;
-                var this_zero_run: ?ZeroRun = null;
-                for (native_endian_parts) |part, i| {
-                    if (part == 0) {
-                        if (this_zero_run) |*zr| {
-                            zr.count += 1;
-                        } else {
-                            this_zero_run = ZeroRun{
-                                .index = i,
-                                .count = 1,
-                            };
-                        }
-                    } else if (this_zero_run) |zr| {
-                        if (longest_zero_run) |lzr| {
-                            if (zr.count > lzr.count and zr.count > 1) {
-                                longest_zero_run = zr;
-                            }
-                        } else {
-                            longest_zero_run = zr;
-                        }
-                    }
-                }
                 try output(context, "[");
                 var i: usize = 0;
-                while (i < native_endian_parts.len) {
-                    if (i != 0) try output(context, ":");
-
-                    if (longest_zero_run) |lzr| {
-                        if (lzr.index == i) {
-                            i += lzr.count;
-                            continue;
+                var abbrv = false;
+                while (i < native_endian_parts.len) : (i += 1) {
+                    if (native_endian_parts[i] == 0) {
+                        if (!abbrv) {
+                            try output(context, if (i == 0) "::" else ":");
+                            abbrv = true;
                         }
+                        continue;
+                    }
+                    try std.fmt.format(context, Errors, output, "{x}", native_endian_parts[i]);
+                    if (i != native_endian_parts.len - 1) {
+                        try output(context, ":");
                     }
-
-                    const part = native_endian_parts[i];
-                    try std.fmt.format(context, Errors, output, "{x}", part);
-                    i += 1;
                 }
                 try std.fmt.format(context, Errors, output, "]:{}", port);
             },