Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion lib/std/net.zig
Original file line number Diff line number Diff line change
Expand Up @@ -683,17 +683,50 @@ pub const Ip6Address = extern struct {
break :blk buf;
},
};

// Find the longest zero run
var longest_start: usize = 8;
var longest_len: usize = 0;
var current_start: usize = 0;
var current_len: usize = 0;

for (native_endian_parts, 0..) |part, i| {
if (part == 0) {
if (current_len == 0) {
current_start = i;
}
current_len += 1;
if (current_len > longest_len) {
longest_start = current_start;
longest_len = current_len;
}
} else {
current_len = 0;
}
}

// Only compress if the longest zero run is 2 or more
if (longest_len < 2) {
longest_start = 8;
longest_len = 0;
}

try out_stream.writeAll("[");
var i: usize = 0;
var abbrv = false;
while (i < native_endian_parts.len) : (i += 1) {
if (native_endian_parts[i] == 0) {
if (i == longest_start) {
// Emit "::" for the longest zero run
if (!abbrv) {
try out_stream.writeAll(if (i == 0) "::" else ":");
abbrv = true;
}
i += longest_len - 1; // Skip the compressed range
continue;
}
if (abbrv) {
abbrv = false;
}
try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]});
if (i != native_endian_parts.len - 1) {
try out_stream.writeAll(":");
Expand Down
56 changes: 56 additions & 0 deletions lib/std/net/test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,62 @@ test "parse and render IP addresses at comptime" {
}
}

test "format IPv6 address with no zero runs" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;

const addr = try std.net.Address.parseIp6("2001:db8:1:2:3:4:5:6", 0);

var buffer: [50]u8 = undefined;
const result = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable;

try std.testing.expectEqualStrings("[2001:db8:1:2:3:4:5:6]:0", result);
}

test "parse IPv6 addresses and check compressed form" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;

const alloc = testing.allocator;

// 1) Parse an IPv6 address that should compress to [2001:db8::1:0:0:2]:0
const addr1 = try std.net.Address.parseIp6("2001:0db8:0000:0000:0001:0000:0000:0002", 0);

// 2) Parse an IPv6 address that should compress to [2001:db8::1:2]:0
const addr2 = try std.net.Address.parseIp6("2001:0db8:0000:0000:0000:0000:0001:0002", 0);

// 3) Parse an IPv6 address that should compress to [2001:db8:1:0:1::2]:0
const addr3 = try std.net.Address.parseIp6("2001:0db8:0001:0000:0001:0000:0000:0002", 0);

// Print each address in Zig's default "[ipv6]:port" form.
const printed1 = try std.fmt.allocPrint(alloc, "{any}", .{addr1});
defer testing.allocator.free(printed1);
const printed2 = try std.fmt.allocPrint(alloc, "{any}", .{addr2});
defer testing.allocator.free(printed2);
const printed3 = try std.fmt.allocPrint(alloc, "{any}", .{addr3});
defer testing.allocator.free(printed3);

// Check the exact compressed forms we expect.
try std.testing.expectEqualStrings("[2001:db8::1:0:0:2]:0", printed1);
try std.testing.expectEqualStrings("[2001:db8::1:2]:0", printed2);
try std.testing.expectEqualStrings("[2001:db8:1:0:1::2]:0", printed3);
}

test "parse IPv6 address, check raw bytes" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;

const expected_raw: [16]u8 = .{
0x20, 0x01, 0x0d, 0xb8, // 2001:db8
0x00, 0x00, 0x00, 0x00, // :0000:0000
0x00, 0x01, 0x00, 0x00, // :0001:0000
0x00, 0x00, 0x00, 0x02, // :0000:0002
};

const addr = try std.net.Address.parseIp6("2001:db8:0000:0000:0001:0000:0000:0002", 0);

const actual_raw = addr.in6.sa.addr[0..];
try std.testing.expectEqualSlices(u8, expected_raw[0..], actual_raw);

}

test "parse and render IPv6 addresses" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;

Expand Down