silk.uri.percent.
Percent-encode bytes for a URI component, and decode %XX escapes.
When to use#
Use UriPercent.encodeOwned to put data, such as a search term or a file name, into a
component of a URI. Use UriPercent.decodeOwned or UriPercent.decodeOrBorrow to get the
bytes back. The ...Into, appendEncoded, and decodeInPlace functions reuse storage that you
already have, and UriPercent.encodeToWriter writes to a Writer.
Details#
The PercentComponent selects which bytes stay as they are. All other bytes become %XX with
uppercase hexadecimal digits. A % in the input is always encoded, so the input must be raw
data, not text that is already encoded. Decoding is strict: each % must start a valid escape.
The decoded bytes are not always valid UTF-8. Check them with String.fromUtf8 if you need
text.
Errors#
DecodeErrorif the text has a%without two hexadecimal digits after it.offsetis the position of the%.OutOfMemoryErrorfrom the functions that allocate.WriterErrorfromUriPercent.encodeToWriter.
Gotchas#
This is generic URI encoding, not HTML form encoding. A + is not decoded to a space, and a
space is encoded as %20. Query keeps &, =, and +, so use Unreserved to encode one
key or value of a query string.
Examples#
Encode a query value and decode it again#
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.collections.bytes { Bytes }
import silk.core.effect { Effect }
import silk.core.result { Result }
import silk.text.string { String }
import silk.uri.percent { DecodeError, PercentComponent, UriPercent }
pub effect fn main() -> i32
! OutOfMemoryError {
let mut allocator = Allocator.systemAllocatorProvider()
let encoded = run UriPercent.encodeOwned(b"tea & cake", PercentComponent.Unreserved)
|> Effect.provideMut<Allocator>(&mut allocator)
if String.view(&encoded) != "tea%20%26%20cake" {
return 1
}
let decoded = run UriPercent.decodeOwned(String.view(&encoded))
|> Effect.provideMut<Allocator>(&mut allocator)
return match move decoded {
Result<Bytes, DecodeError>.Success { value } => {
let bytes = Bytes.asSlice(&value)
if bytes.length != 10 || bytes[4] != 38 {
return 1
}
return 0
}
Result<Bytes, DecodeError>.Failure { error: _ } => 1
}
}Import as UriPercent with import silk.uri.percent { UriPercent }.
Public declarations: 4.
PercentComponent#
pub enum PercentComponentThe URI component that the encoded bytes go into. It selects the bytes that stay as they are.
All components keep ASCII letters, digits, -, ., _, and ~. The other components also
keep the sub-delimiters !$&'()*+,;=, except where the variant says otherwise.
Gotchas#
Query keeps &, =, and +, so use Unreserved to encode one key or value of a query
string.
Userinfo#
UserinfoUser information. It keeps : and encodes @.
RegName#
RegNameA registered host name. It encodes : and @.
PathSegment#
PathSegmentOne path segment. It keeps : and @, and encodes /.
FirstPathSegment#
FirstPathSegmentThe first segment of a relative path. It encodes : and /, so the segment cannot look like
a scheme.
Path#
PathA full path. It keeps /, :, and @, and encodes ? and #.
Unreserved#
UnreservedOnly the unreserved bytes. It encodes all sub-delimiters. Use it for one key or value of a query string.
Query#
QueryA full query. It keeps /, ?, :, @, &, and =.
Fragment#
FragmentA full fragment. It keeps /, ?, :, and @.
DecodeError#
pub struct DecodeErrorA % that does not start a valid escape.
Field offset#
pub offset: usizeThe position of the %, from the start of the input.
Implementation Debug for DecodeError#
impl Debug for DecodeErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the fields, as DecodeError { offset: ... }.
Decoded#
pub union Decoded<'input>The result of UriPercent.decodeOrBorrow: the input itself, or new decoded bytes.
The bytes are not always valid UTF-8.
Borrowed#
BorrowedThe input has no %, so it is returned as it is. Nothing is allocated.
Field values#
pub values: &'input [u8]The input bytes.
Owned#
OwnedThe input has escapes. The decoded bytes are in new storage.
Field value#
pub value: BytesThe decoded bytes.
UriPercent#
pub struct UriPercentFunctions that percent-encode and decode URI components.
Details#
The PercentComponent selects which bytes stay as they are. All other bytes become %XX with
uppercase hexadecimal digits. A % in the input is always encoded, so the input must be raw
data, not text that is already encoded. Decoding is strict: each % must start a valid escape.
The decoded bytes are not always valid UTF-8.
Gotchas#
This is generic URI encoding, not HTML form encoding. A + is not decoded to a space, and a
space is encoded as %20.
Implementation UriPercent#
impl UriPercentAssociated function encodeOwned#
pub effect fn encodeOwned(values: &[u8], component: PercentComponent) -> String
! OutOfMemoryError
? &mut AllocatorReturns values encoded for component, in a new string.
It allocates exactly the needed size, and nothing for an empty result.
Errors#
OutOfMemoryErrorif theAllocatorfails or the size does not fit inusize.
Gotchas#
Path keeps :. If the path of a relative reference can start with a segment that contains
:, encode that segment with FirstPathSegment.
Associated function encodeInto#
pub effect fn encodeInto(output: &mut Bytes, values: &[u8], component: PercentComponent) -> ()
! OutOfMemoryError
? &mut AllocatorReplaces the contents of output with values encoded for component.
If output has enough capacity, it does not allocate.
Errors#
OutOfMemoryErrorif theAllocatorfails or the size does not fit inusize.outputdoes not change.
Associated function encodedLength#
pub fn encodedLength(values: &[u8], component: PercentComponent) -> Option<usize>Returns the length of values encoded for component, or None if it does not fit in
usize.
Each encoded byte counts as 3. Use it to reserve space for a URI that you build from more than one component.
Associated function appendEncoded#
pub effect fn appendEncoded(output: &mut Bytes, values: &[u8], component: PercentComponent) -> ()
! OutOfMemoryError
? &mut AllocatorAdds values encoded for component to the end of output.
The existing bytes of output do not change. If output has enough capacity, it does not
allocate.
Errors#
OutOfMemoryErrorif theAllocatorfails or the size does not fit inusize.outputdoes not change.
Associated function encodeToWriter#
pub effect fn encodeToWriter(values: &[u8], component: PercentComponent) -> ()
! WriterError
? &mut WriterWrites values encoded for component to the ambient Writer. It does not allocate.
Errors#
WriterErrorif theWriterfails. The bytes that it accepted stay written.
Gotchas#
It does not flush the Writer.
Associated function decodeOwned#
pub effect fn decodeOwned(text: string) -> Result<Bytes, DecodeError>
! OutOfMemoryError
? &mut AllocatorReturns the bytes of text with each %XX escape decoded, in new storage.
A + stays a +. It allocates exactly the needed size, and nothing for an empty result.
Errors#
Failurewith aDecodeErrorif an escape is not valid. Nothing is allocated.OutOfMemoryErrorif theAllocatorfails.
Associated function decodeInto#
pub effect fn decodeInto(output: &mut Bytes, text: string) -> Result<(), DecodeError>
! OutOfMemoryError
? &mut AllocatorReplaces the contents of output with the decoded bytes of text.
A + stays a +. If output has enough capacity, it does not allocate.
Errors#
Failurewith aDecodeErrorif an escape is not valid.outputdoes not change.OutOfMemoryErrorif theAllocatorfails.outputdoes not change.
Associated function decodeInPlace#
pub fn decodeInPlace(values: &mut [u8]) -> Result<usize, DecodeError>Decodes the escapes of values in place, and returns the length of the decoded bytes.
The decoded bytes are at the start of values. It does not allocate.
Errors#
Failurewith aDecodeErrorif an escape is not valid.valuesdoes not change.
Gotchas#
The bytes after the returned length are not specified. The length of the slice does not change.
Associated function decodeOrBorrow#
pub effect fn decodeOrBorrow<'input>(text: string<'input>) -> Result<Decoded<'input>, DecodeError>
! OutOfMemoryError
? &mut AllocatorReturns text as it is if it has no %, or else the decoded bytes in new storage.
Use it when most input has no escapes, to skip the allocation and the copy.
Errors#
Failurewith aDecodeErrorif an escape is not valid. Nothing is allocated.OutOfMemoryErrorif theAllocatorfails.
Category: Data formats.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2.