Skip to content

Commit cdf3b06

Browse files
committed
use ByteView for improved performance
1 parent 4b30bcd commit cdf3b06

File tree

4 files changed

+71
-52
lines changed

4 files changed

+71
-52
lines changed

lightbug_http/address.mojo

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -383,19 +383,19 @@ fn is_ipv6(network: NetworkType) -> Bool:
383383
"""Check if the network type is IPv6."""
384384
return network in (NetworkType.tcp6, NetworkType.udp6, NetworkType.ip6)
385385

386-
fn resolve_localhost(host: String, network: NetworkType) -> String:
386+
fn resolve_localhost(host: ByteView[StaticConstantOrigin], network: NetworkType) -> ByteView[StaticConstantOrigin]:
387387
"""Resolve localhost to the appropriate IP address based on network type."""
388-
if host != AddressConstants.LOCALHOST:
388+
if host != AddressConstants.LOCALHOST.as_bytes():
389389
return host
390390

391391
if network.is_ipv4():
392-
return AddressConstants.IPV4_LOCALHOST
392+
return AddressConstants.IPV4_LOCALHOST.as_bytes()
393393
elif network.is_ipv6():
394-
return AddressConstants.IPV6_LOCALHOST
394+
return AddressConstants.IPV6_LOCALHOST.as_bytes()
395395

396396
return host
397397

398-
fn parse_ipv6_bracketed_address(address: ByteView[ImmutableAnyOrigin]) raises -> (ByteView[ImmutableAnyOrigin], UInt16):
398+
fn parse_ipv6_bracketed_address(address: ByteView[StaticConstantOrigin]) raises -> (ByteView[StaticConstantOrigin], UInt16):
399399
"""Parse an IPv6 address enclosed in brackets.
400400
401401
Returns:
@@ -420,32 +420,32 @@ fn parse_ipv6_bracketed_address(address: ByteView[ImmutableAnyOrigin]) raises ->
420420
UInt16(end_bracket_index + 1)
421421
)
422422

423-
fn validate_no_brackets(address: String, start_idx: UInt16, end_idx: Optional[UInt16] = None) raises:
423+
fn validate_no_brackets(address: ByteView[StaticConstantOrigin], start_idx: UInt16, end_idx: Optional[UInt16] = None) raises:
424424
"""Validate that the address segment contains no brackets."""
425-
var segment: String
425+
var segment: ByteView[StaticConstantOrigin]
426426

427427
if end_idx is None:
428428
segment = address[int(start_idx):]
429429
else:
430430
segment = address[int(start_idx):int(end_idx.value())]
431431

432-
if segment.find("[") != -1:
432+
if segment.find(Byte(ord("["))) != -1:
433433
raise Error("unexpected '[' in address")
434-
if segment.find("]") != -1:
434+
if segment.find(Byte(ord("]"))) != -1:
435435
raise Error("unexpected ']' in address")
436436

437-
fn parse_port(port_str: String) raises -> UInt16:
437+
fn parse_port(port_str: ByteView[StaticConstantOrigin]) raises -> UInt16:
438438
"""Parse and validate port number."""
439-
if port_str == AddressConstants.EMPTY:
439+
if port_str == AddressConstants.EMPTY.as_bytes():
440440
raise MissingPortError
441441

442-
var port = int(port_str)
442+
var port = int(str(port_str))
443443
if port < MIN_PORT or port > MAX_PORT:
444444
raise Error("Port number out of range (0-65535)")
445445

446446
return UInt16(port)
447447

448-
fn parse_address(network: NetworkType, address: String) raises -> (String, UInt16):
448+
fn parse_address(network: NetworkType, address: ByteView[StaticConstantOrigin]) raises -> (ByteView[StaticConstantOrigin], UInt16):
449449
"""Parse an address string into a host and port.
450450
451451
Args:
@@ -457,28 +457,28 @@ fn parse_address(network: NetworkType, address: String) raises -> (String, UInt1
457457
"""
458458
if network.is_ip_protocol():
459459
var host = resolve_localhost(address, network)
460-
if host == AddressConstants.EMPTY:
460+
if host == AddressConstants.EMPTY.as_bytes():
461461
raise Error("missing host")
462462

463463
# For IPv6 addresses in IP protocol mode, we need to handle the address as-is
464-
if network == NetworkType.ip6 and host.find(":") != -1:
464+
if network == NetworkType.ip6 and host.find(Byte(ord(":"))) != -1:
465465
return host, DEFAULT_IP_PORT
466466

467467
# For other IP protocols, no colons allowed
468-
if host.find(":") != -1:
468+
if host.find(Byte(ord(":"))) != -1:
469469
raise Error("IP protocol addresses should not include ports")
470470

471471
return host, DEFAULT_IP_PORT
472472

473-
var colon_index = address.rfind(":")
473+
var colon_index = address.rfind(Byte(ord(":")))
474474
if colon_index == -1:
475475
raise MissingPortError
476476

477-
var host: String
477+
var host: ByteView[StaticConstantOrigin]
478478
var bracket_offset: UInt16 = 0
479479

480480
# Handle IPv6 addresses
481-
if address[0] == "[":
481+
if address[0] == Byte(ord("[")):
482482
try:
483483
(host, bracket_offset) = parse_ipv6_bracketed_address(address)
484484
except e:
@@ -488,13 +488,13 @@ fn parse_address(network: NetworkType, address: String) raises -> (String, UInt1
488488
else:
489489
# For IPv4, simply split at the last colon
490490
host = address[:colon_index]
491-
if host.find(":") != -1:
491+
if host.find(Byte(ord(":"))) != -1:
492492
raise TooManyColonsError
493493

494494
var port = parse_port(address[colon_index + 1:])
495495

496496
host = resolve_localhost(host, network)
497-
if host == AddressConstants.EMPTY:
497+
if host == AddressConstants.EMPTY.as_bytes():
498498
raise Error("missing host")
499499

500500
return host, port

lightbug_http/io/bytes.mojo

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ struct ByteView[origin: Origin]():
146146

147147
fn __iter__(self) -> _SpanIter[Byte, origin]:
148148
return self._inner.__iter__()
149-
149+
150150
fn find(self, target: Byte) -> Int:
151151
"""Finds the index of a byte in a byte span.
152152
@@ -162,6 +162,24 @@ struct ByteView[origin: Origin]():
162162

163163
return -1
164164

165+
fn rfind(self, target: Byte) -> Int:
166+
"""Finds the index of the last occurrence of a byte in a byte span.
167+
168+
Args:
169+
target: The byte to find.
170+
171+
Returns:
172+
The index of the last occurrence of the byte in the span, or -1 if not found.
173+
"""
174+
# Start from the end and work backwards
175+
var i = len(self) - 1
176+
while i >= 0:
177+
if self[i] == target:
178+
return i
179+
i -= 1
180+
181+
return -1
182+
165183
fn to_bytes(self) -> Bytes:
166184
return Bytes(self._inner)
167185

tests/lightbug_http/io/test_bytes.mojo

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import testing
22
from collections import Dict, List
3-
from lightbug_http.io.bytes import Bytes, bytes
3+
from lightbug_http.io.bytes import Bytes, ByteView, bytes
44

55

66
fn test_string_literal_to_bytes() raises:
@@ -35,3 +35,4 @@ fn test_string_to_bytes() raises:
3535

3636
for c in cases.items():
3737
testing.assert_equal(Bytes(c[].key.as_bytes()), c[].value)
38+

tests/lightbug_http/test_host_port.mojo

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,63 +4,63 @@ from lightbug_http.address import TCPAddr, NetworkType, join_host_port, parse_ad
44

55
def test_split_host_port():
66
# TCP4
7-
var hp = parse_address(NetworkType.tcp4, "127.0.0.1:8080")
8-
testing.assert_equal(hp[0], "127.0.0.1")
7+
var hp = parse_address(NetworkType.tcp4, "127.0.0.1:8080".as_bytes())
8+
testing.assert_equal(hp[0], "127.0.0.1".as_bytes())
99
testing.assert_equal(hp[1], 8080)
1010

1111
# TCP4 with localhost
12-
hp = parse_address(NetworkType.tcp4, "localhost:8080")
13-
testing.assert_equal(hp[0], "127.0.0.1")
12+
hp = parse_address(NetworkType.tcp4, "localhost:8080".as_bytes())
13+
testing.assert_equal(hp[0], "127.0.0.1".as_bytes())
1414
testing.assert_equal(hp[1], 8080)
1515

1616
# TCP6
17-
hp = parse_address(NetworkType.tcp6, "[::1]:8080")
18-
testing.assert_equal(hp[0], "::1")
17+
hp = parse_address(NetworkType.tcp6, "[::1]:8080".as_bytes())
18+
testing.assert_equal(hp[0], "::1".as_bytes())
1919
testing.assert_equal(hp[1], 8080)
2020

2121
# TCP6 with localhost
22-
hp = parse_address(NetworkType.tcp6, "localhost:8080")
23-
testing.assert_equal(hp[0], "::1")
22+
hp = parse_address(NetworkType.tcp6, "localhost:8080".as_bytes())
23+
testing.assert_equal(hp[0], "::1".as_bytes())
2424
testing.assert_equal(hp[1], 8080)
2525

2626
# UDP4
27-
hp = parse_address(NetworkType.udp4, "192.168.1.1:53")
28-
testing.assert_equal(hp[0], "192.168.1.1")
27+
hp = parse_address(NetworkType.udp4, "192.168.1.1:53".as_bytes())
28+
testing.assert_equal(hp[0], "192.168.1.1".as_bytes())
2929
testing.assert_equal(hp[1], 53)
3030

3131
# UDP4 with localhost
32-
hp = parse_address(NetworkType.udp4, "localhost:53")
33-
testing.assert_equal(hp[0], "127.0.0.1")
32+
hp = parse_address(NetworkType.udp4, "localhost:53".as_bytes())
33+
testing.assert_equal(hp[0], "127.0.0.1".as_bytes())
3434
testing.assert_equal(hp[1], 53)
3535

3636
# UDP6
37-
hp = parse_address(NetworkType.udp6, "[2001:db8::1]:53")
38-
testing.assert_equal(hp[0], "2001:db8::1")
37+
hp = parse_address(NetworkType.udp6, "[2001:db8::1]:53".as_bytes())
38+
testing.assert_equal(hp[0], "2001:db8::1".as_bytes())
3939
testing.assert_equal(hp[1], 53)
4040

4141
# UDP6 with localhost
42-
hp = parse_address(NetworkType.udp6, "localhost:53")
43-
testing.assert_equal(hp[0], "::1")
42+
hp = parse_address(NetworkType.udp6, "localhost:53".as_bytes())
43+
testing.assert_equal(hp[0], "::1".as_bytes())
4444
testing.assert_equal(hp[1], 53)
4545

4646
# IP4 (no port)
47-
hp = parse_address(NetworkType.ip4, "192.168.1.1")
48-
testing.assert_equal(hp[0], "192.168.1.1")
47+
hp = parse_address(NetworkType.ip4, "192.168.1.1".as_bytes())
48+
testing.assert_equal(hp[0], "192.168.1.1".as_bytes())
4949
testing.assert_equal(hp[1], 0)
5050

5151
# IP4 with localhost
52-
hp = parse_address(NetworkType.ip4, "localhost")
53-
testing.assert_equal(hp[0], "127.0.0.1")
52+
hp = parse_address(NetworkType.ip4, "localhost".as_bytes())
53+
testing.assert_equal(hp[0], "127.0.0.1".as_bytes())
5454
testing.assert_equal(hp[1], 0)
5555

5656
# IP6 (no port)
57-
hp = parse_address(NetworkType.ip6, "2001:db8::1")
58-
testing.assert_equal(hp[0], "2001:db8::1")
57+
hp = parse_address(NetworkType.ip6, "2001:db8::1".as_bytes())
58+
testing.assert_equal(hp[0], "2001:db8::1".as_bytes())
5959
testing.assert_equal(hp[1], 0)
6060

6161
# IP6 with localhost
62-
hp = parse_address(NetworkType.ip6, "localhost")
63-
testing.assert_equal(hp[0], "::1")
62+
hp = parse_address(NetworkType.ip6, "localhost".as_bytes())
63+
testing.assert_equal(hp[0], "::1".as_bytes())
6464
testing.assert_equal(hp[1], 0)
6565

6666
# TODO: IPv6 long form - Not supported yet.
@@ -71,35 +71,35 @@ def test_split_host_port():
7171
# Error cases
7272
# IP protocol with port
7373
try:
74-
_ = parse_address(NetworkType.ip4, "192.168.1.1:80")
74+
_ = parse_address(NetworkType.ip4, "192.168.1.1:80".as_bytes())
7575
testing.assert_false("Should have raised an error for IP protocol with port")
7676
except Error:
7777
testing.assert_true(True)
7878

7979
# Missing port
8080
try:
81-
_ = parse_address(NetworkType.tcp4, "192.168.1.1")
81+
_ = parse_address(NetworkType.tcp4, "192.168.1.1".as_bytes())
8282
testing.assert_false("Should have raised MissingPortError")
8383
except MissingPortError:
8484
testing.assert_true(True)
8585

8686
# Missing port
8787
try:
88-
_ = parse_address(NetworkType.tcp6, "[::1]")
88+
_ = parse_address(NetworkType.tcp6, "[::1]".as_bytes())
8989
testing.assert_false("Should have raised MissingPortError")
9090
except MissingPortError:
9191
testing.assert_true(True)
9292

9393
# Port out of range
9494
try:
95-
_ = parse_address(NetworkType.tcp4, "192.168.1.1:70000")
95+
_ = parse_address(NetworkType.tcp4, "192.168.1.1:70000".as_bytes())
9696
testing.assert_false("Should have raised error for invalid port")
9797
except Error:
9898
testing.assert_true(True)
9999

100100
# Missing closing bracket
101101
try:
102-
_ = parse_address(NetworkType.tcp6, "[::1:8080")
102+
_ = parse_address(NetworkType.tcp6, "[::1:8080".as_bytes())
103103
testing.assert_false("Should have raised error for missing bracket")
104104
except Error:
105105
testing.assert_true(True)

0 commit comments

Comments
 (0)