5 Commits
v2.0.3 ... main

Author SHA1 Message Date
af619a54da docs: Updated changelog
Some checks failed
test / test (push) Has been cancelled
2025-10-27 19:19:06 +00:00
e51116c2b2 fix: Fix uri encoding/decoding and add new tests
Some checks failed
test / test (push) Has been cancelled
2025-10-27 19:13:37 +00:00
56e3682237 build: Update version & repository 2025-10-27 19:13:37 +00:00
7f631dc13c perf: Changed benchmarks titles 2025-10-27 19:13:37 +00:00
91807aac61 perf: Removed erlang/js specific parsing
Some checks failed
test / test (push) Has been cancelled
This currently seems to compile exactly the same in 1.12.0 and 1.13.0 so
therefore has a performance regression
2025-10-19 19:16:33 +01:00
7 changed files with 42 additions and 195 deletions

View File

@@ -22,3 +22,8 @@
- Minor performance improvement for erlang - Minor performance improvement for erlang
- Major performance improvement for js - Major performance improvement for js
## 2.0.4
- Reverted some optimisations as they are unnecessary for Gleam v1.14.0+
- Fix uri encoding/decoding (I think)

View File

@@ -1,12 +1,12 @@
name = "gluri" name = "gluri"
version = "2.0.3" version = "2.0.4"
# Fill out these fields if you intend to generate HTML documentation or publish # Fill out these fields if you intend to generate HTML documentation or publish
# your project to the Hex package manager. # your project to the Hex package manager.
# #
description = "Uri (RFC 3986) library for Gleam" description = "Uri (RFC 3986) library for Gleam"
licences = ["Apache-2.0"] licences = ["Apache-2.0"]
repository = { type = "github", user = "pendletong", repo = "uri" } repository = { type = "gitea", host = "git.pendleton.ie", user = "pendletong", repo = "uri" }
links = [{ title = "RFC 3986", href = "https://www.ietf.org/rfc/rfc3986.txt" }] links = [{ title = "RFC 3986", href = "https://www.ietf.org/rfc/rfc3986.txt" }]
# #
# For a full reference of all the available options, you can have a look at # For a full reference of all the available options, you can have a look at

View File

@@ -3,7 +3,7 @@
packages = [ packages = [
{ name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" },
{ name = "benchee", version = "1.4.0", build_tools = ["mix"], requirements = ["deep_merge", "statistex", "table"], otp_app = "benchee", source = "hex", outer_checksum = "299CD10DD8CE51C9EA3DDB74BB150F93D25E968F93E4C1FA31698A8E4FA5D715" }, { name = "benchee", version = "1.5.0", build_tools = ["mix"], requirements = ["deep_merge", "statistex", "table"], otp_app = "benchee", source = "hex", outer_checksum = "5B075393AEA81B8AE74EADD1C28B1D87E8A63696C649D8293DB7C4DF3EB67535" },
{ name = "bigben", version = "1.0.1", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "bigben", source = "hex", outer_checksum = "190E489610A80D76C48BACC75EB8314BD184FF0220AB0F251ABE760B993B91BB" }, { name = "bigben", version = "1.0.1", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "bigben", source = "hex", outer_checksum = "190E489610A80D76C48BACC75EB8314BD184FF0220AB0F251ABE760B993B91BB" },
{ name = "birl", version = "1.8.0", build_tools = ["gleam"], requirements = ["gleam_regexp", "gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "2AC7BA26F998E3DFADDB657148BD5DDFE966958AD4D6D6957DD0D22E5B56C400" }, { name = "birl", version = "1.8.0", build_tools = ["gleam"], requirements = ["gleam_regexp", "gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "2AC7BA26F998E3DFADDB657148BD5DDFE966958AD4D6D6957DD0D22E5B56C400" },
{ name = "deep_merge", version = "1.0.0", build_tools = ["mix"], requirements = [], otp_app = "deep_merge", source = "hex", outer_checksum = "CE708E5F094B9CD4E8F2BE4F00D2F4250C4095BE93F8CD6D018C753894885430" }, { name = "deep_merge", version = "1.0.0", build_tools = ["mix"], requirements = [], otp_app = "deep_merge", source = "hex", outer_checksum = "CE708E5F094B9CD4E8F2BE4F00D2F4250C4095BE93F8CD6D018C753894885430" },
@@ -14,9 +14,9 @@ packages = [
{ name = "gleam_erlang", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "1124AD3AA21143E5AF0FC5CF3D9529F6DB8CA03E43A55711B60B6B7B3874375C" }, { name = "gleam_erlang", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "1124AD3AA21143E5AF0FC5CF3D9529F6DB8CA03E43A55711B60B6B7B3874375C" },
{ name = "gleam_javascript", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_javascript", source = "hex", outer_checksum = "EF6C77A506F026C6FB37941889477CD5E4234FCD4337FF0E9384E297CB8F97EB" }, { name = "gleam_javascript", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_javascript", source = "hex", outer_checksum = "EF6C77A506F026C6FB37941889477CD5E4234FCD4337FF0E9384E297CB8F97EB" },
{ name = "gleam_json", version = "3.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "874FA3C3BB6E22DD2BB111966BD40B3759E9094E05257899A7C08F5DE77EC049" }, { name = "gleam_json", version = "3.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "874FA3C3BB6E22DD2BB111966BD40B3759E9094E05257899A7C08F5DE77EC049" },
{ name = "gleam_otp", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "7987CBEBC8060B88F14575DEF546253F3116EBE2A5DA6FD82F38243FCE97C54B" }, { name = "gleam_otp", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "BA6A294E295E428EC1562DC1C11EA7530DCB981E8359134BEABC8493B7B2258E" },
{ name = "gleam_regexp", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "9C215C6CA84A5B35BB934A9B61A9A306EC743153BE2B0425A0D032E477B062A9" }, { name = "gleam_regexp", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "9C215C6CA84A5B35BB934A9B61A9A306EC743153BE2B0425A0D032E477B062A9" },
{ name = "gleam_stdlib", version = "0.63.2", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "962B25C667DA07F4CAB32001F44D3C41C1A89E58E3BBA54F183B482CF6122150" }, { name = "gleam_stdlib", version = "0.65.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "7C69C71D8C493AE11A5184828A77110EB05A7786EBF8B25B36A72F879C3EE107" },
{ name = "gleam_time", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_time", source = "hex", outer_checksum = "DCDDC040CE97DA3D2A925CDBBA08D8A78681139745754A83998641C8A3F6587E" }, { name = "gleam_time", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_time", source = "hex", outer_checksum = "DCDDC040CE97DA3D2A925CDBBA08D8A78681139745754A83998641C8A3F6587E" },
{ name = "gleam_yielder", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_yielder", source = "hex", outer_checksum = "8E4E4ECFA7982859F430C57F549200C7749823C106759F4A19A78AEA6687717A" }, { name = "gleam_yielder", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_yielder", source = "hex", outer_checksum = "8E4E4ECFA7982859F430C57F549200C7749823C106759F4A19A78AEA6687717A" },
{ name = "gleeunit", version = "1.6.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "FDC68A8C492B1E9B429249062CD9BAC9B5538C6FBF584817205D0998C42E1DAC" }, { name = "gleeunit", version = "1.6.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "FDC68A8C492B1E9B429249062CD9BAC9B5538C6FBF584817205D0998C42E1DAC" },

View File

@@ -729,25 +729,6 @@ fn parse_unreserved(str: String) -> Result(#(String, String), Nil) {
// / "*" / "+" / "," / ";" / "=" // / "*" / "+" / "," / ";" / "="
// %21 / %24 / %26 / %27 / %28 / %29 // %21 / %24 / %26 / %27 / %28 / %29
// / %2A / %2B / %2C / %3B / %3D // / %2A / %2B / %2C / %3B / %3D
@target(erlang)
fn parse_sub_delim(str: String) -> Result(#(String, String), Nil) {
case string.pop_grapheme(str) {
Ok(#("!" as char, tail))
| Ok(#("$" as char, tail))
| Ok(#("&" as char, tail))
| Ok(#("'" as char, tail))
| Ok(#("(" as char, tail))
| Ok(#(")" as char, tail))
| Ok(#("*" as char, tail))
| Ok(#("+" as char, tail))
| Ok(#("," as char, tail))
| Ok(#(";" as char, tail))
| Ok(#("=" as char, tail)) -> Ok(#(char, tail))
_ -> Error(Nil)
}
}
@target(javascript)
fn parse_sub_delim(str: String) -> Result(#(String, String), Nil) { fn parse_sub_delim(str: String) -> Result(#(String, String), Nil) {
case str { case str {
"!" as char <> tail "!" as char <> tail
@@ -766,25 +747,6 @@ fn parse_sub_delim(str: String) -> Result(#(String, String), Nil) {
} }
// DIGIT = %x3039 // DIGIT = %x3039
@target(erlang)
fn parse_digit(str: String) -> Result(#(String, String), Nil) {
case string.pop_grapheme(str) {
Ok(#("0" as char, tail))
| Ok(#("1" as char, tail))
| Ok(#("2" as char, tail))
| Ok(#("3" as char, tail))
| Ok(#("4" as char, tail))
| Ok(#("5" as char, tail))
| Ok(#("6" as char, tail))
| Ok(#("7" as char, tail))
| Ok(#("8" as char, tail))
| Ok(#("9" as char, tail)) -> Ok(#(char, tail))
_ -> Error(Nil)
}
}
@target(javascript)
fn parse_digit(str: String) -> Result(#(String, String), Nil) { fn parse_digit(str: String) -> Result(#(String, String), Nil) {
case str { case str {
"0" as char <> tail "0" as char <> tail
@@ -803,24 +765,6 @@ fn parse_digit(str: String) -> Result(#(String, String), Nil) {
} }
// DIGIT (non-zero) = %x3139 // DIGIT (non-zero) = %x3139
@target(erlang)
fn parse_digit_nz(str: String) -> Result(#(String, String), Nil) {
case string.pop_grapheme(str) {
Ok(#("1" as char, tail))
| Ok(#("2" as char, tail))
| Ok(#("3" as char, tail))
| Ok(#("4" as char, tail))
| Ok(#("5" as char, tail))
| Ok(#("6" as char, tail))
| Ok(#("7" as char, tail))
| Ok(#("8" as char, tail))
| Ok(#("9" as char, tail)) -> Ok(#(char, tail))
_ -> Error(Nil)
}
}
@target(javascript)
fn parse_digit_nz(str: String) -> Result(#(String, String), Nil) { fn parse_digit_nz(str: String) -> Result(#(String, String), Nil) {
case str { case str {
"1" as char <> tail "1" as char <> tail
@@ -847,67 +791,6 @@ fn parse_digits(str: String, digits: String) {
} }
// ALPHA = %x415A | %x617A // ALPHA = %x415A | %x617A
@target(erlang)
fn parse_alpha(str: String) -> Result(#(String, String), Nil) {
case string.pop_grapheme(str) {
Ok(#("a" as char, tail))
| Ok(#("b" as char, tail))
| Ok(#("c" as char, tail))
| Ok(#("d" as char, tail))
| Ok(#("e" as char, tail))
| Ok(#("f" as char, tail))
| Ok(#("g" as char, tail))
| Ok(#("h" as char, tail))
| Ok(#("i" as char, tail))
| Ok(#("j" as char, tail))
| Ok(#("k" as char, tail))
| Ok(#("l" as char, tail))
| Ok(#("m" as char, tail))
| Ok(#("n" as char, tail))
| Ok(#("o" as char, tail))
| Ok(#("p" as char, tail))
| Ok(#("q" as char, tail))
| Ok(#("r" as char, tail))
| Ok(#("s" as char, tail))
| Ok(#("t" as char, tail))
| Ok(#("u" as char, tail))
| Ok(#("v" as char, tail))
| Ok(#("w" as char, tail))
| Ok(#("x" as char, tail))
| Ok(#("y" as char, tail))
| Ok(#("z" as char, tail))
| Ok(#("A" as char, tail))
| Ok(#("B" as char, tail))
| Ok(#("C" as char, tail))
| Ok(#("D" as char, tail))
| Ok(#("E" as char, tail))
| Ok(#("F" as char, tail))
| Ok(#("G" as char, tail))
| Ok(#("H" as char, tail))
| Ok(#("I" as char, tail))
| Ok(#("J" as char, tail))
| Ok(#("K" as char, tail))
| Ok(#("L" as char, tail))
| Ok(#("M" as char, tail))
| Ok(#("N" as char, tail))
| Ok(#("O" as char, tail))
| Ok(#("P" as char, tail))
| Ok(#("Q" as char, tail))
| Ok(#("R" as char, tail))
| Ok(#("S" as char, tail))
| Ok(#("T" as char, tail))
| Ok(#("U" as char, tail))
| Ok(#("V" as char, tail))
| Ok(#("W" as char, tail))
| Ok(#("X" as char, tail))
| Ok(#("Y" as char, tail))
| Ok(#("Z" as char, tail)) -> Ok(#(char, tail))
_ -> Error(Nil)
}
}
@target(javascript)
fn parse_alpha(str: String) -> Result(#(String, String), Nil) { fn parse_alpha(str: String) -> Result(#(String, String), Nil) {
case str { case str {
"a" as char <> tail "a" as char <> tail

View File

@@ -109,7 +109,7 @@ pub fn parse_min_max(
do_parse_min_max(str, "", min, max, parse_fn) do_parse_min_max(str, "", min, max, parse_fn)
} }
pub fn do_parse_min_max( fn do_parse_min_max(
str: d, str: d,
acc: String, acc: String,
min: Int, min: Int,
@@ -370,37 +370,6 @@ fn unescape_percent(str: String) -> String {
} }
} }
@target(erlang)
pub fn parse_hex_digit(str: String) -> Result(#(String, String), Nil) {
case string.pop_grapheme(str) {
Ok(#("0" as char, tail))
| Ok(#("1" as char, tail))
| Ok(#("2" as char, tail))
| Ok(#("3" as char, tail))
| Ok(#("4" as char, tail))
| Ok(#("5" as char, tail))
| Ok(#("6" as char, tail))
| Ok(#("7" as char, tail))
| Ok(#("8" as char, tail))
| Ok(#("9" as char, tail))
| Ok(#("a" as char, tail))
| Ok(#("b" as char, tail))
| Ok(#("c" as char, tail))
| Ok(#("d" as char, tail))
| Ok(#("e" as char, tail))
| Ok(#("f" as char, tail))
| Ok(#("A" as char, tail))
| Ok(#("B" as char, tail))
| Ok(#("C" as char, tail))
| Ok(#("D" as char, tail))
| Ok(#("E" as char, tail))
| Ok(#("F" as char, tail)) -> Ok(#(char, tail))
_ -> Error(Nil)
}
}
@target(javascript)
pub fn parse_hex_digit(str: String) -> Result(#(String, String), Nil) { pub fn parse_hex_digit(str: String) -> Result(#(String, String), Nil) {
case str { case str {
"0" as char <> tail "0" as char <> tail
@@ -434,14 +403,6 @@ pub fn parse_hex_digits(str, min, max) {
parse_min_max(str, min, max, parse_hex_digit) parse_min_max(str, min, max, parse_hex_digit)
} }
fn encoding_not_needed(i: Int) -> Bool {
// $-_.+!*'()
case i {
36 | 45 | 95 | 46 | 43 | 33 | 42 | 39 | 40 | 41 -> True
_ -> False
}
}
fn is_unreserved_char(i: Int) -> Bool { fn is_unreserved_char(i: Int) -> Bool {
case i { case i {
45 | 46 | 95 | 126 -> True 45 | 46 | 95 | 126 -> True
@@ -691,13 +652,13 @@ pub fn do_percent_encode(str: String) -> String {
fn encode_codepoint(codepoint: Int) -> String { fn encode_codepoint(codepoint: Int) -> String {
case codepoint <= 127 { case codepoint <= 127 {
True -> { True -> {
case is_unreserved_char(codepoint) || encoding_not_needed(codepoint) { case is_unreserved_char(codepoint) {
True -> { True -> {
let assert Ok(cpnt) = string.utf_codepoint(codepoint) let assert Ok(cpnt) = string.utf_codepoint(codepoint)
string.from_utf_codepoints([cpnt]) string.from_utf_codepoints([cpnt])
} }
False -> { False -> {
"%" <> int.to_base16(codepoint) "%" <> string.pad_start(int.to_base16(codepoint), 2, "0")
} }
} }
} }

View File

@@ -89,11 +89,15 @@ pub fn parse_benchmark() {
pub fn pop_benchmark() { pub fn pop_benchmark() {
benchmark.run( benchmark.run(
[ [
benchmark.Function("pop", fn(data) { fn() { pop(data, "") } }), benchmark.Function("pop with range", fn(data) { fn() { pop(data, "") } }),
benchmark.Function("pop2", fn(data) { fn() { pop4(data, "") } }), benchmark.Function("pop check char", fn(data) { fn() { pop4(data, "") } }),
benchmark.Function("pop3", fn(data) { fn() { pop5(data, "") } }), benchmark.Function("pop check result", fn(data) {
benchmark.Function("match", fn(data) { fn() { pop2(data, "") } }), fn() { pop5(data, "") }
benchmark.Function("match_2", fn(data) { fn() { pop3(data, "") } }), }),
benchmark.Function("letter as var <> tail", fn(data) {
fn() { pop2(data, "") }
}),
benchmark.Function("letter <> tail", fn(data) { fn() { pop3(data, "") } }),
], ],
[ [
// benchmark.Data("long", "abcdefghijklmnopqrstuvwxyz"), // benchmark.Data("long", "abcdefghijklmnopqrstuvwxyz"),

View File

@@ -1146,37 +1146,31 @@ pub fn equivalence_tests() {
]) ])
} }
const percent_codec_fixtures = [ const percent_encode_examples = [
#(" ", "%20"), #("", ""),
#(",", "%2C"), #("%", "%25"),
#(";", "%3B"), #("%%", "%25%25"),
#(":", "%3A"), #(" \r\n\t\u{B}\f", "%20%0D%0A%09%0B%0C"),
#("!", "!"), #(
#("?", "%3F"), "-_.~0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
#("'", "'"), "-_.~0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
#("(", "("), ),
#(")", ")"), #("\u{0}", "%00"),
#("[", "%5B"), #("abc\u{00}def", "abc%00def"),
#("@", "%40"),
#("/", "%2F"),
#("\\", "%5C"),
#("&", "%26"), #("&", "%26"),
#("#", "%23"), #(
#("=", "%3D"), "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
#("~", "~"), "%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E_%60%7B%7C%7D~",
#("ñ", "%C3%B1"), ),
#("-", "-"), #("“Aha”", "%E2%80%9CAha%E2%80%9D"),
#("_", "_"), #("\u{201C}Aha\u{201D}", "%E2%80%9CAha%E2%80%9D"),
#(".", "."), #("*+,=>/", "%2A%2B%2C%3D%3E%2F"),
#("*", "*"),
#("+", "+"),
#("100% great+fun", "100%25%20great+fun"),
] ]
pub fn percent_encode_tests() { pub fn percent_encode_tests() {
describe("percent encoding", [ describe("percent encoding", [
it("encoding", fn() { it("encoding", fn() {
percent_codec_fixtures percent_encode_examples
|> list.map(fn(t) { |> list.map(fn(t) {
let #(a, b) = t let #(a, b) = t
uri.percent_encode(a) uri.percent_encode(a)
@@ -1185,7 +1179,7 @@ pub fn percent_encode_tests() {
Nil Nil
}), }),
it("decoding", fn() { it("decoding", fn() {
percent_codec_fixtures percent_encode_examples
|> list.map(fn(t) { |> list.map(fn(t) {
let #(a, b) = t let #(a, b) = t
uri.percent_decode(b) uri.percent_decode(b)