Skip to content

Commit

Permalink
Add test cases and fix for compression of long zero runs in ipv6
Browse files Browse the repository at this point in the history
  • Loading branch information
bondehagen committed Jan 7, 2025
1 parent 40f5eac commit 70542a0
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 1 deletion.
34 changes: 33 additions & 1 deletion lib/std/net.zig
Original file line number Diff line number Diff line change
Expand Up @@ -673,27 +673,59 @@ pub const Ip6Address = extern struct {
return;
}
const big_endian_parts = @as(*align(1) const [8]u16, @ptrCast(&self.sa.addr));

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

const native_endian_parts = switch (native_endian) {
.big => big_endian_parts.*,
.little => blk: {
var buf: [8]u16 = undefined;
for (big_endian_parts, 0..) |part, i| {
buf[i] = mem.bigToNative(u16, part);

if (buf[i] == 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;
}
}
break :blk buf;
},
};

// 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
54 changes: 54 additions & 0 deletions lib/std/net/test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,60 @@ 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 = std.heap.page_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});
const printed2 = try std.fmt.allocPrint(alloc, "{any}", .{addr2});
const printed3 = try std.fmt.allocPrint(alloc, "{any}", .{addr3});

// 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);

std.debug.print("Raw bytes match expected for address {any}\n", .{addr});
}

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

Expand Down

0 comments on commit 70542a0

Please sign in to comment.