aboutsummaryrefslogtreecommitdiff
path: root/core/net
diff options
context:
space:
mode:
authorJeroen van Rijn <Kelimion@users.noreply.github.com>2026-02-11 21:21:48 +0100
committerJeroen van Rijn <Kelimion@users.noreply.github.com>2026-02-11 21:21:48 +0100
commit43f4b2187cc362a3abba71efc73d05a085c57f04 (patch)
treebf12c419be87b1b78c6abaacf0bfe29b393993e9 /core/net
parentea6867ed613433640523333146d4a77d23059cf7 (diff)
Fix #6251
Introduces `strings.Builder` versions of the various `to_string` procedures. These are used using a stack buffer for `address_to_string` as called by `endpoint_to_string`, requiring no temp allocation for the address that's then written into the endpoint's builder.
Diffstat (limited to 'core/net')
-rw-r--r--core/net/addr.odin122
1 files changed, 76 insertions, 46 deletions
diff --git a/core/net/addr.odin b/core/net/addr.odin
index d29b46b65..888744841 100644
--- a/core/net/addr.odin
+++ b/core/net/addr.odin
@@ -459,38 +459,63 @@ split_port :: proc(endpoint_str: string) -> (addr_or_host: string, port: int, ok
return
}
-// Joins an address or hostname with a port.
-join_port :: proc(address_or_host: string, port: int, allocator := context.allocator) -> string {
+// Joins an address or hostname with a port, allocated using an Allocator.
+join_port_allocator :: proc(address_or_host: string, port: int, allocator := context.allocator) -> string {
addr_or_host, _, ok := split_port(address_or_host)
if !ok {
return addr_or_host
}
b := strings.builder_make(allocator)
-
addr := parse_address(addr_or_host)
if addr == nil {
// hostname
strings.write_string(&b, addr_or_host)
strings.write_string(&b, ":")
strings.write_int(&b, port)
+ return strings.to_string(b)
} else {
- switch _ in addr {
- case IP4_Address:
- strings.write_string(&b, address_to_string(addr))
- strings.write_string(&b, ":")
- strings.write_int(&b, port)
- case IP6_Address:
- strings.write_string(&b, "[")
- strings.write_string(&b, address_to_string(addr))
- strings.write_string(&b, "]:")
- strings.write_int(&b, port)
- }
+ return _join_port_internal(addr, port, &b)
}
- return strings.to_string(b)
}
+// Joins an address or hostname with a port, allocated using a `strings.Builder`.
+join_port_builder :: proc(address_or_host: string, port: int, b: ^strings.Builder) -> string {
+ addr_or_host, _, ok := split_port(address_or_host)
+ if !ok {
+ return addr_or_host
+ }
+
+ addr := parse_address(addr_or_host)
+ if addr == nil {
+ // hostname
+ strings.write_string(b, addr_or_host)
+ strings.write_string(b, ":")
+ strings.write_int(b, port)
+ return strings.to_string(b^)
+ } else {
+ return _join_port_internal(addr, port, b)
+ }
+}
+join_port :: proc{join_port_allocator, join_port_builder}
+
+// Also used in `endpoint_to_string_builder`.
+@(private)
+_join_port_internal :: proc(addr: Address, port: int, b: ^strings.Builder) -> string {
+ switch a in addr {
+ case IP4_Address:
+ address_to_string_builder(addr, b)
+ strings.write_string(b, ":")
+ strings.write_int(b, port)
+ case IP6_Address:
+ strings.write_string(b, "[")
+ address_to_string_builder(addr, b)
+ strings.write_string(b, "]:")
+ strings.write_int(b, port)
+ }
+ return strings.to_string(b^)
+}
// TODO(tetra): Do we need this?
map_to_ip6 :: proc(addr: Address) -> Address {
@@ -510,17 +535,26 @@ map_to_ip6 :: proc(addr: Address) -> Address {
See RFC 5952 section 4 for IPv6 representation recommendations.
*/
-address_to_string :: proc(addr: Address, allocator := context.temp_allocator) -> string {
+address_to_string_allocator :: proc(addr: Address, allocator := context.temp_allocator) -> string {
b := strings.builder_make(allocator)
+ return address_to_string_builder(addr, &b)
+}
+
+/*
+ Returns a string representation of the address using a `strings.Builder`.
+
+ See RFC 5952 section 4 for IPv6 representation recommendations.
+*/
+address_to_string_builder :: proc(addr: Address, b: ^strings.Builder) -> string {
switch v in addr {
case IP4_Address:
- strings.write_uint(&b, uint(v[0]))
- strings.write_byte(&b, '.')
- strings.write_uint(&b, uint(v[1]))
- strings.write_byte(&b, '.')
- strings.write_uint(&b, uint(v[2]))
- strings.write_byte(&b, '.')
- strings.write_uint(&b, uint(v[3]))
+ strings.write_uint(b, uint(v[0]))
+ strings.write_byte(b, '.')
+ strings.write_uint(b, uint(v[1]))
+ strings.write_byte(b, '.')
+ strings.write_uint(b, uint(v[2]))
+ strings.write_byte(b, '.')
+ strings.write_uint(b, uint(v[3]))
case IP6_Address:
// First find the longest run of zeroes.
Zero_Run :: struct {
@@ -574,7 +608,7 @@ address_to_string :: proc(addr: Address, allocator := context.temp_allocator) ->
for val, i in v {
if best.start == i || best.end == i {
// For the left and right side of the best zero run, print a `:`.
- strings.write_string(&b, ":")
+ strings.write_string(b, ":")
} else if i < best.start {
/*
If we haven't made it to the best run yet, print the digit.
@@ -584,10 +618,10 @@ address_to_string :: proc(addr: Address, allocator := context.temp_allocator) ->
buf: [32]byte
str := strconv.write_bits(buf[:], u64(val), 16, false, size_of(val), strconv.digits, {})
- strings.write_string(&b, str)
+ strings.write_string(b, str)
if i < best.start - 1 {
- strings.write_string(&b, ":")
+ strings.write_string(b, ":")
}
} else if i > best.end {
/*
@@ -597,42 +631,38 @@ address_to_string :: proc(addr: Address, allocator := context.temp_allocator) ->
buf: [32]byte
str := strconv.write_bits(buf[:], u64(val), 16, false, size_of(val), strconv.digits, {})
- strings.write_string(&b, str)
+ strings.write_string(b, str)
if i != 7 {
- strings.write_string(&b, ":")
+ strings.write_string(b, ":")
}
}
}
}
- return strings.to_string(b)
+ return strings.to_string(b^)
}
+address_to_string :: proc{address_to_string_allocator, address_to_string_builder}
// Returns a temporarily-allocated string representation of the endpoint.
// If there's a port, uses the `ip4address:port` or `[ip6address]:port` format, respectively.
-endpoint_to_string :: proc(ep: Endpoint, allocator := context.temp_allocator) -> string {
+endpoint_to_string_allocator :: proc(ep: Endpoint, allocator := context.temp_allocator) -> string {
+ b := strings.builder_make(allocator)
+ return endpoint_to_string_builder(ep, &b)
+}
+
+// Returns a string representation of the endpoint using a `strings.Builder`.
+// If there's a port, uses the `ip4address:port` or `[ip6address]:port` format, respectively.
+endpoint_to_string_builder :: proc(ep: Endpoint, b: ^strings.Builder) -> string {
if ep.port == 0 {
- return address_to_string(ep.address, allocator)
+ return address_to_string(ep.address, b)
} else {
- s := address_to_string(ep.address, context.temp_allocator)
- b := strings.builder_make(allocator)
- switch a in ep.address {
- case IP4_Address:
- strings.write_string(&b, s)
- strings.write_string(&b, ":")
- strings.write_int(&b, ep.port)
- case IP6_Address:
- strings.write_string(&b, "[")
- strings.write_string(&b, s)
- strings.write_string(&b, "]:")
- strings.write_int(&b, ep.port)
- }
- return strings.to_string(b)
+ return _join_port_internal(ep.address, ep.port, b)
}
}
-to_string :: proc{address_to_string, endpoint_to_string}
+endpoint_to_string :: proc{endpoint_to_string_allocator, endpoint_to_string_builder}
+to_string :: proc{address_to_string_allocator, address_to_string_builder, endpoint_to_string_allocator, endpoint_to_string_builder}
family_from_address :: proc(addr: Address) -> Address_Family {
switch _ in addr {