diff --git a/README.md b/README.md index a1e08b8..d08caf4 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,13 @@ still takes from CHANGELOG.md is which release is current, which reaches the masthead, the home page, the install page and llms.txt through `scripts/releases.mjs`. +The blog under `/blog/` is the one authored section about a moment: a post per +release, in `src/content/blog/`. A post names its release in its frontmatter, +and what it quotes of that release's notes `scripts/notes.mjs` reads out of +CHANGELOG.md at the release's tag, so the next release does not change it. +Write the post after the site has been regenerated at the release (below): +the build refuses a post for a release CHANGELOG.md does not yet state. + ## Building The scripts look for an iyi checkout beside this one, named `iyi`, or wherever diff --git a/doc/STACK.md b/doc/STACK.md index b6ba8a5..3ff5d95 100644 --- a/doc/STACK.md +++ b/doc/STACK.md @@ -48,6 +48,8 @@ site/ of INSTALL.md, writes generated/targets.json releases.mjs reads CHANGELOG.md's release headings, writes generated/releases.json + notes.mjs reads the section of CHANGELOG.md each post names, + at that release's tag, writes generated/notes.json samples.mjs transcludes samples/iyi, samples/tour, records/break and records/agent into generated/samples/ record-agent.mjs recorder: runs the agent loop against a real @@ -61,10 +63,10 @@ site/ src/ styles/ tokens.css, base.css, code.css components/ Measure, Stamped, DurationChart, Sample, Quote, - Compare, Frame, Lifted + Compare, Frame, Lifted, ReleaseNote, Heading layouts/Page.astro masthead, colophon, fonts, styles, scheme switch pages/ index, why, agents, playground, learn, install, - download, targets, project + download, targets, project, blog playground/ the slot: types, registry, engines generated/ build output, gitignored, never edited ``` @@ -132,6 +134,17 @@ The two classes of number are rendered differently by design; see repository changes on the site. Renaming a sample a lesson names fails the build. - **Generated, measured:** all numbers, via the pipeline above. +- **Authored, quoting a release at its tag:** the posts under `/blog/`, in + `src/content/blog/`. A post is the one page about a moment rather than about + the tree as it is. Its `release` must be one CHANGELOG.md states and its + `date` no earlier than that release's (`src/lib/posts.ts`), and what it + quotes of the release's notes is read by `scripts/notes.mjs` out of + CHANGELOG.md at the release's tag and set by `ReleaseNote` with the lines it + came from, so a later edit to the changelog does not move a sentence in an + announcement. A listing or a command a post sets goes through `Sample` and + `Quote` like any other page's and follows the current release; a later + release that removes what one anchors on fails the build at that post. + `/blog/rss.xml` is built from the same list the index renders. - **Authored fresh:** the marketing pages and the lessons. SPEC.md is a design record, not teaching material, so the learning path is written by hand but is grounded in real samples and real README passages, and every factual claim in diff --git a/package.json b/package.json index 9bdeaba..d563d62 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "private": true, "type": "module", "scripts": { - "generate": "node scripts/fonts.mjs && node scripts/facts.mjs && node scripts/targets.mjs && node scripts/proofs.mjs && node scripts/releases.mjs && node scripts/modified.mjs && node scripts/install-targets.mjs && node scripts/samples.mjs && node scripts/thesis.mjs && node scripts/project.mjs && node scripts/download.mjs && node scripts/records.mjs && node scripts/stdlib.mjs && node scripts/contrast.mjs && node scripts/no-transcription.mjs && node scripts/blocker.mjs", + "generate": "node scripts/fonts.mjs && node scripts/facts.mjs && node scripts/targets.mjs && node scripts/proofs.mjs && node scripts/releases.mjs && node scripts/notes.mjs && node scripts/modified.mjs && node scripts/install-targets.mjs && node scripts/samples.mjs && node scripts/thesis.mjs && node scripts/project.mjs && node scripts/download.mjs && node scripts/records.mjs && node scripts/stdlib.mjs && node scripts/contrast.mjs && node scripts/no-transcription.mjs && node scripts/blocker.mjs", "generate:install-targets": "node scripts/install-targets.mjs", "record:wasm": "node scripts/record-wasm.mjs", "record:diagnostics": "node scripts/record-diagnostics.mjs", diff --git a/records/agent.json b/records/agent.json index 6447e61..d740855 100644 --- a/records/agent.json +++ b/records/agent.json @@ -1,10 +1,10 @@ { "recorded": { - "compiler": "iyi 0.20.1 (built on Crystal 1.22.0-dev [91d012b1e] (2026-11-04))", - "commit": "91d012b1ed978c7df5cc033624ec161a2daf01b6", + "compiler": "iyi 0.21.0 (built on Crystal 1.22.0-dev [08ce6c1cb] (2026-11-09))", + "commit": "08ce6c1cb2ba5768b15485dbbd6413fcb17df19a", "machine": "Linux 7.0.0-38-generic x86_64, QEMU Virtual CPU version 2.5+", "command": "npm run record:agent", - "when": "2026-11-04T04:32:53.960Z" + "when": "2026-11-09T17:40:45.851Z" }, "project": [ { @@ -44,7 +44,7 @@ "command": "iyi mod context --json app.iyi", "exitCode": 0, "payload": "stdout", - "stdout": "{\"file\":\"agent/app.iyi\",\"imports\":[{\"import\":\"app/greeter\",\"api\":{\"module\":\"app/greeter\",\"interface_hash\":\"f2f9576ba7e3996e204d5c93ccf5f42d\",\"functions\":[{\"name\":\"polite\",\"parameters\":[\"name : String\"],\"returns\":\"String\",\"doc\":\"The exported surface carries its doc comment, so the grounding pack an agent\\nreads says what the function is for and not only what it takes.\",\"rendered\":\"def polite(name : String) : String\"},{\"name\":\"shout\",\"parameters\":[\"name : String\"],\"returns\":\"String\",\"rendered\":\"def shout(name : String) : String\"}],\"types\":[{\"name\":\"Greet\",\"kind\":\"trait\",\"visibility\":\"pub\",\"methods\":[{\"name\":\"greet\",\"parameters\":[],\"returns\":\"String\",\"required\":true,\"rendered\":\"abstract def greet : String\"}]}],\"impls\":[]}}]}\n", + "stdout": "{\"file\":\"agent/app.iyi\",\"imports\":[{\"import\":\"app/greeter\",\"api\":{\"module\":\"app/greeter\",\"interface_hash\":\"dadbf4541512680f291862fd67e8fbcf\",\"functions\":[{\"name\":\"polite\",\"parameters\":[\"name : String\"],\"returns\":\"String\",\"doc\":\"The exported surface carries its doc comment, so the grounding pack an agent\\nreads says what the function is for and not only what it takes.\",\"rendered\":\"def polite(name : String) : String\"},{\"name\":\"shout\",\"parameters\":[\"name : String\"],\"returns\":\"String\",\"rendered\":\"def shout(name : String) : String\"}],\"types\":[{\"name\":\"Greet\",\"kind\":\"trait\",\"visibility\":\"pub\",\"methods\":[{\"name\":\"greet\",\"parameters\":[],\"returns\":\"String\",\"required\":true,\"rendered\":\"abstract def greet : String\"}]}],\"impls\":[]}}]}\n", "stderr": "" }, { @@ -104,7 +104,7 @@ "command": "iyi test --json .", "exitCode": 0, "payload": "stdout", - "stdout": "{\"tests\":[{\"file\":\"./app_test.iyi\",\"status\":\"pass\",\"seconds\":0.281}],\"passed\":1,\"failed\":0,\"skipped\":0}\n", + "stdout": "{\"tests\":[{\"file\":\"./app_test.iyi\",\"status\":\"pass\",\"seconds\":0.336}],\"passed\":1,\"failed\":0,\"skipped\":0}\n", "stderr": "" }, { @@ -135,7 +135,7 @@ "bytes": 731, "sourceBytes": 707, "interfaceHashes": { - "app/greeter": "f2f9576ba7e3996e204d5c93ccf5f42d" + "app/greeter": "dadbf4541512680f291862fd67e8fbcf" } }, "suggested": [ @@ -167,7 +167,7 @@ }, "serverInfo": { "name": "iyi", - "version": "0.20.1" + "version": "0.21.0" } }, "tools": [ diff --git a/records/diagnostics.json b/records/diagnostics.json index 029a842..7a2842e 100644 --- a/records/diagnostics.json +++ b/records/diagnostics.json @@ -1,10 +1,10 @@ { "recorded": { - "compiler": "iyi 0.20.1 (built on Crystal 1.22.0-dev [91d012b1e] (2026-11-04))", - "commit": "91d012b1ed978c7df5cc033624ec161a2daf01b6", + "compiler": "iyi 0.21.0 (built on Crystal 1.22.0-dev [08ce6c1cb] (2026-11-09))", + "commit": "08ce6c1cb2ba5768b15485dbbd6413fcb17df19a", "machine": "Linux 7.0.0-38-generic x86_64, QEMU Virtual CPU version 2.5+", "command": "npm run record:diagnostics", - "when": "2026-11-04T04:32:47.944Z" + "when": "2026-11-09T17:40:39.969Z" }, "cases": [ { @@ -122,7 +122,7 @@ "path": "records/break/share_reach.iyi", "command": "IYI_PATH=/src iyi build --no-codegen --no-color --error-on-warnings records/break/share_reach.iyi", "exitCode": 1, - "stderr": "In records/break/share_reach.iyi:19:24\n\n 19 | Server.serve(listener) do |req|\n ^\nWarning: the block Server.serve keeps, which may run on other threads reaches `GREETING : Array(String)` (not Share) through `greeting` → `GREETING.join`: Array(String)'s field @size is assigned in `append`. If it is only read, make it a `List(String)` (std/list), which is Share. Keep the value behind a `Mutex` (std/mutex), or, when every thread may reach it safely - it is set once and then only read, or guarded some other way - say why on the constant: `@[SafeReach(\"...\")]` (SPEC.md III.4.4)\n\nA total of 1 warnings were found.\n" + "stderr": "In records/break/share_reach.iyi:19:24\n\n 19 | Server.serve(listener) do |req|\n ^\nWarning: the block Server.serve keeps, which may run on other threads reaches `GREETING : Array(String)` (not Share) through `greeting` → `GREETING.join`: Array(String)'s field @size is assigned in `append`. If it is only read, make it a `List(String)` (std/list), which is Share. Keep the value behind a `Mutex` (std/mutex), or, when every thread may reach it safely - every write is before the first parallel task starts, or behind one `Mutex#lock` on a constant or a class variable - say why on the constant: `@[SafeReach(\"...\")]`, which the compiler proves (SPEC.md III.4.4)\n\nA total of 1 warnings were found.\n" } ] } diff --git a/records/highlight.json b/records/highlight.json index f2a119a..3aa1249 100644 --- a/records/highlight.json +++ b/records/highlight.json @@ -1,10 +1,10 @@ { "recorded": { - "compiler": "iyi 0.20.1 (built on Crystal 1.22.0-dev [91d012b1e] (2026-11-04))", - "commit": "91d012b1ed978c7df5cc033624ec161a2daf01b6", + "compiler": "iyi 0.21.0 (built on Crystal 1.22.0-dev [08ce6c1cb] (2026-11-09))", + "commit": "08ce6c1cb2ba5768b15485dbbd6413fcb17df19a", "machine": "Linux 7.0.0-38-generic x86_64, QEMU Virtual CPU version 2.5+", "command": "npm run record:highlight", - "when": "2026-11-04T04:32:57.045Z" + "when": "2026-11-09T17:40:49.124Z" }, "files": { "samples/iyi/app/formal.iyi": "# A second module, exporting a `title` of its own. See `../modules.iyi`.\nmodule app/formal\n\npub def title : String\n "esteemed colleague"\nend\n\npub def address(name : String) : String\n "Good day, #{name}."\nend\n", @@ -15,10 +15,10 @@ "samples/iyi/calc/ast.iyi": "module calc/ast\n\n# What an evaluated program can go wrong with. Both are ordinary members of the\n# union an evaluation returns (SPEC.md III.1), so a caller either handles them\n# or propagates with `!`.\npub struct UnknownName\n getter name : String\n\n def initialize(@name : String)\n end\nend\n\nimpl Error for UnknownName\n def message : String\n "unknown name '#{name}'"\n end\nend\n\npub struct DividedByZero\n def initialize\n end\nend\n\nimpl Error for DividedByZero\n def message : String\n "divided by zero"\n end\nend\n\n# The one thing every node can do. A trait rather than a base class, because\n# there is nothing to inherit and R-3 has no opinion to break here.\npub trait Node\n abstract def evaluate(scope : Hash(String, Int32)) : Int32 | UnknownName | DividedByZero\nend\n\npub struct Number\n getter value : Int32\n\n def initialize(@value : Int32)\n end\nend\n\nimpl Node for Number\n def evaluate(scope : Hash(String, Int32)) : Int32 | UnknownName | DividedByZero\n value\n end\nend\n\npub struct Name\n getter name : String\n\n def initialize(@name : String)\n end\nend\n\nimpl Node for Name\n def evaluate(scope : Hash(String, Int32)) : Int32 | UnknownName | DividedByZero\n return UnknownName.new(name) unless scope.has_key?(name)\n scope[name]\n end\nend\n\npub class Binary\n getter op : Char\n getter left : Number | Name | Binary\n getter right : Number | Name | Binary\n\n def initialize(@op : Char, @left : Number | Name | Binary, @right : Number | Name | Binary)\n end\nend\n\nimpl Node for Binary\n def evaluate(scope : Hash(String, Int32)) : Int32 | UnknownName | DividedByZero\n a = left.evaluate(scope)!\n b = right.evaluate(scope)!\n\n if op == '+'\n a + b\n elsif op == '-'\n a - b\n elsif op == '*'\n a * b\n else\n return DividedByZero.new if b == 0\n a // b\n end\n end\nend\n", "samples/iyi/calc/lexer.iyi": "module calc/lexer\n\n# What the scanner produces. A token is its kind, the text it covers and where\n# it started, because an error that cannot say where is half an error.\npub struct Token\n getter kind : String\n getter text : String\n getter at : Int32\n\n def initialize(@kind : String, @text : String, @at : Int32)\n end\nend\n\npub struct BadCharacter\n getter at : Int32\n getter found : Char\n\n def initialize(@at : Int32, @found : Char)\n end\nend\n\nimpl Error for BadCharacter\n def message : String\n "unexpected character at #{at}"\n end\nend\n\npub def scan(source : String) : Array(Token) | BadCharacter\n tokens = [] of Token\n index = 0\n\n while index < source.size\n c = source[index]\n\n # A line ends in CRLF on Windows: `echo 2 + 3 * 4| iyi run\n # samples\\iyi\\calc.iyi` in cmd answered "unexpected character at 9",\n # the `\\r`.\n if c == ' ' || c == '\\n' || c == '\\t' || c == '\\r'\n index = index + 1\n elsif digit?(c)\n start = index\n while index < source.size && digit?(source[index])\n index = index + 1\n end\n tokens << Token.new("number", source[start, index - start], start)\n elsif letter?(c)\n start = index\n while index < source.size && (letter?(source[index]) || digit?(source[index]))\n index = index + 1\n end\n tokens << Token.new("name", source[start, index - start], start)\n elsif operator?(c)\n tokens << Token.new("op", source[index, 1], index)\n index = index + 1\n else\n return BadCharacter.new(index, c)\n end\n end\n\n tokens << Token.new("end", "", index)\n tokens\nend\n\ndef digit?(c : Char) : Bool\n c >= '0' && c <= '9'\nend\n\ndef letter?(c : Char) : Bool\n (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'\nend\n\ndef operator?(c : Char) : Bool\n c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')' || c == '='\nend\n", "samples/iyi/calc/parser.iyi": "module calc/parser\n\nimport calc/lexer::{Token}\nimport calc/ast::{Number, Name, Binary}\n\npub struct SyntaxError\n getter at : Int32\n getter wanted : String\n\n def initialize(@at : Int32, @wanted : String)\n end\nend\n\nimpl Error for SyntaxError\n def message : String\n "expected #{wanted} at #{at}"\n end\nend\n\n# One statement: `name = expression`, or an expression on its own, which the\n# runner prints. Kept as a pair rather than a node type, because a statement is\n# not something this language evaluates twice.\npub struct Statement\n getter target : String\n getter body : Number | Name | Binary\n\n def initialize(@target : String, @body : Number | Name | Binary)\n end\nend\n\n# A cursor over the tokens. A struct with an index rather than a shifting array,\n# because backing up one token is the parser's whole trick.\npub class Reader\n getter tokens : Array(Token)\n getter at : Int32\n\n def initialize(@tokens : Array(Token))\n @at = 0\n end\n\n def peek : Token\n tokens[at]\n end\n\n def advance : Token\n token = tokens[at]\n @at = at + 1\n token\n end\nend\n\npub def parse(tokens : Array(Token)) : Array(Statement) | SyntaxError\n reader = Reader.new(tokens)\n statements = [] of Statement\n\n while reader.peek.kind != "end"\n statements << statement(reader)!\n end\n\n statements\nend\n\ndef statement(reader : Reader) : Statement | SyntaxError\n first = reader.peek\n\n if first.kind == "name" && reader.tokens[reader.at + 1].text == "="\n reader.advance\n reader.advance\n Statement.new(first.text, expression(reader)!)\n else\n Statement.new("", expression(reader)!)\n end\nend\n\ndef expression(reader : Reader) : Number | Name | Binary | SyntaxError\n left = term(reader)!\n\n while reader.peek.kind == "op" && (reader.peek.text == "+" || reader.peek.text == "-")\n op = reader.advance.text[0]\n left = Binary.new(op, left, term(reader)!)\n end\n\n left\nend\n\ndef term(reader : Reader) : Number | Name | Binary | SyntaxError\n left = factor(reader)!\n\n while reader.peek.kind == "op" && (reader.peek.text == "*" || reader.peek.text == "/")\n op = reader.advance.text[0]\n left = Binary.new(op, left, factor(reader)!)\n end\n\n left\nend\n\ndef factor(reader : Reader) : Number | Name | Binary | SyntaxError\n token = reader.advance\n\n if token.kind == "number"\n Number.new(token.text.to_i)\n elsif token.kind == "name"\n Name.new(token.text)\n elsif token.text == "("\n inside = expression(reader)!\n closing = reader.advance\n return SyntaxError.new(closing.at, "')'") unless closing.text == ")"\n inside\n else\n SyntaxError.new(token.at, "a number, a name or '('")\n end\nend\n", - "samples/iyi/calc.iyi": "# A language, in the language — the biggest program this prelude can hold.\n#\n# echo "x = 2 + 3 * 4\n# x * 10" | ./bin/iyi run samples/iyi/calc.iyi\n#\n# The other samples each document a rule. This one documents nothing and is\n# here for a different reason: everything else in `samples/iyi` is a page long,\n# and a language that has only been used for pages has not been used. This is\n# three modules and a scanner, a parser and an evaluator, the shape of every\n# real program that reads something, written against iyi's own 25,763-line\n# library and nothing else.\n#\n# What it cost to write is recorded where it landed. `String#[]`, `String#[](start,\n# count)`, `String#to_i` and `read_input` are in the prelude because this file\n# asked for them, which is the rule the prelude grows by (SPEC.md "0.1.0",\n# item 3). Nothing else was missing.\nmodule samples/calc\n\nimport calc/lexer::{scan, BadCharacter}\nimport calc/parser::{parse, Statement, SyntaxError}\nimport calc/ast::{Number, Name, Binary, UnknownName, DividedByZero}\n\n# What runs when nothing is piped in, so that the sample is a program rather\n# than a prompt. CI runs it this way. Its last two lines are errors on\n# purpose, and the demo exits 0 with them.\nDEMO = "x = 2 + 3 * 4\ny = (x - 4) / 2\nx + y * 10\nmissing + 1\n1 / 0"\n\n# Whether every statement answered. An error goes to standard error, and\n# input with one makes the program exit 1: all of them went to standard\n# output at exit 0, so `echo 1 / 0 | iyi run samples/iyi/calc.iyi && next`\n# ran `next`.\npub def run(source : String) : Bool\n tokens = scan(source)\n if tokens.is_a?(BadCharacter)\n stderr.puts "error: #{tokens.message}"\n return false\n end\n\n program = parse(tokens)\n if program.is_a?(SyntaxError)\n stderr.puts "error: #{program.message}"\n return false\n end\n\n answered = true\n scope = {} of String => Int32\n program.each do |statement|\n value = statement.body.evaluate(scope)\n\n if value.is_a?(Int32)\n if statement.target.empty?\n puts value\n else\n scope[statement.target] = value\n end\n else\n # `value` is an error member of the union, and every one of them answers\n # `message` because they implement `Error` (SPEC.md III.1).\n stderr.puts "error: #{value.message}"\n answered = false\n end\n end\n answered\nend\n\n# Input that is only line ends is nothing piped in: PowerShell's `"" |`\n# sends a CRLF. And its pipe starts what it sends with a byte order mark,\n# which is not part of the program: "unexpected character at 0".\ntyped = read_input.lchop('\\uFEFF')\nif typed.strip.empty?\n run(DEMO)\nelsif !run(typed)\n exit 1\nend\n", + "samples/iyi/calc.iyi": "# A language, in the language — the biggest program this prelude can hold.\n#\n# echo "x = 2 + 3 * 4\n# x * 10" | ./bin/iyi run samples/iyi/calc.iyi\n#\n# The other samples each document a rule. This one documents nothing and is\n# here for a different reason: everything else in `samples/iyi` is a page long,\n# and a language that has only been used for pages has not been used. This is\n# three modules and a scanner, a parser and an evaluator, the shape of every\n# real program that reads something, written against iyi's own 28,806-line\n# library and nothing else.\n#\n# What it cost to write is recorded where it landed. `String#[]`, `String#[](start,\n# count)`, `String#to_i` and `read_input` are in the prelude because this file\n# asked for them, which is the rule the prelude grows by (SPEC.md "0.1.0",\n# item 3). Nothing else was missing.\nmodule samples/calc\n\nimport calc/lexer::{scan, BadCharacter}\nimport calc/parser::{parse, Statement, SyntaxError}\nimport calc/ast::{Number, Name, Binary, UnknownName, DividedByZero}\n\n# What runs when nothing is piped in, so that the sample is a program rather\n# than a prompt. CI runs it this way. Its last two lines are errors on\n# purpose, and the demo exits 0 with them.\nDEMO = "x = 2 + 3 * 4\ny = (x - 4) / 2\nx + y * 10\nmissing + 1\n1 / 0"\n\n# Whether every statement answered. An error goes to standard error, and\n# input with one makes the program exit 1: all of them went to standard\n# output at exit 0, so `echo 1 / 0 | iyi run samples/iyi/calc.iyi && next`\n# ran `next`.\npub def run(source : String) : Bool\n tokens = scan(source)\n if tokens.is_a?(BadCharacter)\n stderr.puts "error: #{tokens.message}"\n return false\n end\n\n program = parse(tokens)\n if program.is_a?(SyntaxError)\n stderr.puts "error: #{program.message}"\n return false\n end\n\n answered = true\n scope = {} of String => Int32\n program.each do |statement|\n value = statement.body.evaluate(scope)\n\n if value.is_a?(Int32)\n if statement.target.empty?\n puts value\n else\n scope[statement.target] = value\n end\n else\n # `value` is an error member of the union, and every one of them answers\n # `message` because they implement `Error` (SPEC.md III.1).\n stderr.puts "error: #{value.message}"\n answered = false\n end\n end\n answered\nend\n\n# Input that is only line ends is nothing piped in: PowerShell's `"" |`\n# sends a CRLF. And its pipe starts what it sends with a byte order mark,\n# which is not part of the program: "unexpected character at 0".\ntyped = read_input.lchop('\\uFEFF')\nif typed.strip.empty?\n run(DEMO)\nelsif !run(typed)\n exit 1\nend\n", "samples/iyi/collections.iyi": "# The `Enumerable` port, exercised — SPEC.md II.6.\n#\n# ./bin/iyi run samples/iyi/collections.iyi\n#\n# `std/enumerable.iyi` holds the trait. This file implements it twice and calls\n# every method it defines. Both matter: a default method is only typed when it\n# is called, so a trait that merely compiles has proved nothing about whether\n# its bodies are sound.\n#\n# `Nums` and `Words` answer `Elem` differently — `Int32` and `String` — and each\n# gets the same 57 default methods from one `each`. That is the claim II.6\n# makes, running.\n\nmodule samples/collections\n\nimport std/enumerable::{Enumerable}\n\npub struct Nums\n def initialize(@a : Array(Int32))\n end\nend\n\nimpl Enumerable for Nums\n type Elem = Int32\n\n def each(& : Int32 -> Nil) : Nil\n @a.each { |x| yield x }\n end\nend\n\npub struct Words\n def initialize(@a : Array(String))\n end\nend\n\nimpl Enumerable for Words\n type Elem = String\n\n def each(& : String -> Nil) : Nil\n @a.each { |x| yield x }\n end\nend\n\nn = Nums.new([3, 1, 4, 1, 5])\nw = Words.new(["pear", "fig", "plum", "kiwi"])\n\nputs "to_a #{n.to_a.join(",")}"\nputs "size #{n.size}"\nputs "empty? #{n.empty?} #{Nums.new([] of Int32).empty?}"\nputs "all? #{n.all? { |x| x > 0 }}"\nputs "any? #{n.any? { |x| x > 4 }}"\nputs "none? #{n.none? { |x| x > 9 }}"\nputs "one? #{n.one? { |x| x == 4 }}"\nputs "count #{n.count { |x| x == 1 }}"\nputs "find #{n.find { |x| x > 3 }} #{n.find { |x| x > 9 }.inspect}"\nputs "index #{n.index { |x| x == 4 }} #{n.index { |x| x == 9 }.inspect}"\nputs "first?/first #{n.first?} #{n.first}"\nputs "map #{n.map { |x| x * 2 }.join(",")}"\nputs "flat_map #{n.flat_map { |x| [x, x] }.join(",")}"\nputs "map_with_index #{n.map_with_index { |x, i| x * i }.join(",")}"\nputs "select #{n.select { |x| x > 2 }.join(",")}"\nputs "reject #{n.reject { |x| x > 2 }.join(",")}"\nputs "partition #{n.partition { |x| x > 2 }.map(&.join("+")).join(" | ")}"\nputs "take/skip #{n.take(2).join(",")} #{n.skip(3).join(",")}"\nputs "take_while #{n.take_while { |x| x < 4 }.join(",")}"\nputs "skip_while #{n.skip_while { |x| x < 4 }.join(",")}"\nputs "reduce #{n.reduce(0) { |a, x| a + x }}"\nputs "each_with_index #{n.map_with_index { |x, i| i }.join(",")}"\nputs "each_with_object #{n.each_with_object([] of Int32) { |e, o| o << e * 10 }.join(",")}"\nputs "join #{n.join("-")}"\nputs "includes? #{n.includes?(4)} #{n.includes?(9)}"\nputs "max?/max #{n.max?} #{n.max}"\nputs "min?/min #{n.min?} #{n.min}"\nputs "sum/product #{n.sum} #{n.product}"\nputs "sum of nothing #{Nums.new([] of Int32).sum}"\nputs "tally #{n.tally[1]}"\nputs "sorted #{n.sorted.join(",")}"\nputs "max_by #{w.max_by { |s| s.size }}"\nputs "group_by #{w.group_by { |s| s.size }[4].join(",")}"\nputs "index_by #{w.index_by { |s| s.size }[3]}"\n\nputs "each_slice #{n.each_slice(2).map(&.join("+")).join(" ")}"\nputs "each_cons #{n.each_cons(2).map(&.join("+")).join(" ")}"\nputs "chunk_while #{n.chunk_while { |a, b| b >= a }.map(&.join("+")).join(" ")}"\nputs "first(n) #{n.first(3).join(",")}"\nputs "compact_map #{n.compact_map { |x| x > 2 ? x * 10 : nil }.join(",")}"\nputs "find_value #{n.find_value { |x| x > 3 ? "big" : nil }}"\nputs "accumulate #{n.accumulate(0) { |a, x| a + x }.join(",")}"\nputs "reduce? #{n.reduce? { |a, x| a + x }}"\nputs "in_groups_of #{n.in_groups_of(2, 0).map(&.join("+")).join(" ")}"\nputs "min_by #{w.min_by { |s| s.size }}"\nputs "max_of/min_of #{w.max_of { |s| s.size }} #{w.min_of { |s| s.size }}"\nputs "minmax #{n.minmax[0]},#{n.minmax[1]}"\nputs "tally_by #{w.tally_by { |s| s.size }[4]}"\nputs "sum_by #{w.sum_by { |s| s.size }}"\n\n# SPEC II.6 finding 6: one bound replaces `Indexable | Iterable | Iterator`,\n# and `O::Elem` names what the other collection yields. Four numbers for\n# four words: a shorter other collection is refused, as the other library\n# refuses it.\nputs "zip #{Nums.new([3, 1, 4, 1]).zip(w).map { |pair| "#{pair[0]}#{pair[1]}" }.join(" ")}"\n\ncycled = [] of Int32\nNums.new([1, 2]).cycle(2) { |x| cycled << x }\nputs "cycle #{cycled.join(",")}"\n\n# A trait with an associated type used as a type: one function, any implementer.\ndef describe(e : Enumerable) : String\n "#{e.size} items"\nend\n\nputs "as a type #{describe(n)} / #{describe(w)}"\n\n# ---------------------------------------------------------------------------\n# What this sample would report as an error\n# ---------------------------------------------------------------------------\n#\n# * `Words.new([...]).sum` — `sum` is bounded `where Elem : Num` and\n# `String` implements `Comparable` but not `Num`. The error names the element\n# type and the bound rather than failing somewhere inside the method body.\n#\n# * A second `impl Enumerable for Nums` — a trait with an associated type is\n# implementable once per type, because a second answer for `Elem` would make\n# every call on `Nums` ambiguous.\n#\n# * `impl Num for X` with no `impl Comparable for X` before it —\n# `Num : Comparable` requires it, and the impls have to be written in\n# dependency order.\n", "samples/iyi/config.iyi": "# The first program that reads text - a `key = value` file, with the line\n# number in its error.\n#\n# ./bin/iyi run samples/iyi/config.iyi\n#\n# Text comes apart into lines, a line into its two halves around a `=`, and\n# each half loses its whitespace; a line that does not parse is an error\n# value carrying where it was (SPEC.md III.1), not a printed message. Like\n# `basics` and `inventory`, this documents no rule: it is here because the\n# prelude grows only when a program in this repository needs something\n# (SPEC.md 0.1.0 item 3), and cutting text is what the third program does.\nmodule samples/config\n\nstruct ParseError\n property line : Int32\n property message : String\n\n def initialize(@line : Int32, @message : String)\n end\nend\n\nimpl Error for ParseError\n def message : String\n "line #{@line}: #{@message}"\n end\nend\n\ndef parse(text : String) : Hash(String, String) | ParseError\n config = {} of String => String\n text.lines.each_with_index do |raw, index|\n line = raw.strip\n next if line.empty? || line.starts_with?('#')\n eq = line.index('=')\n return ParseError.new(index + 1, "no '=' in #{line.inspect}") unless eq\n key = line[0, eq].strip\n return ParseError.new(index + 1, "empty key") if key.empty?\n config[key] = line[eq + 1, line.size - eq - 1].strip\n end\n config\nend\n\ngood = "# the service\\nname = iyi\\nport= 8080\\n\\nmode =fast\\n"\ncase parse(good)\nin Hash(String, String)\n puts it # => {"name" => "iyi", "port" => "8080", "mode" => "fast"}\n puts it["port"].to_i + 1 # => 8081\n puts it.keys.join(", ") # => name, port, mode\nin ParseError then puts "error: #{it.message}"\nend\n\nbad = "name = iyi\\nbroken line\\n"\ncase parse(bad)\nin Hash(String, String) then puts "parsed"\nin ParseError then puts "error: #{it.message}" # => error: line 2: no '=' in "broken line"\nend\n\n# The pieces a parser cuts, on their own.\nputs "a,,b".split(',') # => ["a", "", "b"]\nputs "one\\ntwo\\n".lines # => ["one", "two"]\nputs " padded\\t".strip.inspect # => "padded"\nputs "config.iyi".ends_with?(".iyi") # => true\n", - "samples/iyi/cores.iyi": "# Work on every core - SPEC.md III.4.13, as a person writes it.\n#\n# ./bin/iyi run samples/iyi/cores.iyi\n#\n# `spawn` is a task on the thread that spawned it, and its block may touch\n# anything; `spawn_parallel` is a task a pool of workers runs on any core,\n# and its block may capture only what is `Share` (III.4.4). std/http's\n# `Server.serve` puts each connection on that pool, so its block is checked\n# the same way. When a block reaches module state through a call, the\n# compiler warns, and `@[SafeReach("why")]` on the state says why it is safe.\nmodule samples/cores\n\nimport std/http::{HTTP, Response, Server}\nimport std/socket::{IyiSocket}\nimport std/mutex::{Mutex}\n\ndef collatz_steps(n : Int64) : Int64\n steps = 0_i64\n while n != 1\n n = n % 2 == 0 ? n // 2 : 3 * n + 1\n steps += 1\n end\n steps\nend\n\n# 1. `spawn` and `spawn_parallel`.\n#\n# The tasks `spawn` starts share their spawner's thread, so they may append\n# to an `Array` they captured. The tasks `spawn_parallel` starts run on\n# whichever core is free; each answers its own value, and a task's answer\n# need not be `Share`, because it is handed over once, after the task ends.\nseen = [] of Int64\ngroup do |g|\n (1..3).each do |n|\n g.spawn do\n seen << collatz_steps(n.to_i64)\n 0\n end\n end\nend\nputs seen.sorted # => [0, 1, 7]\n\nlongest = 0_i64\ngroup do |g|\n handles = [] of IyiTask(Int64)\n 4.times do |part|\n handles << g.spawn_parallel do\n best = 0_i64\n start = part.to_i64 * 25_000 + 1\n (start..start + 24_999).each { |n| best = collatz_steps(n) if collatz_steps(n) > best }\n best\n end\n end\n handles.each do |handle|\n steps = handle.value\n longest = steps if steps.is_a?(Int64) && steps > longest\n end\nend\nputs longest # => 350, the longest chain below 100,000\n\n# 2. State the pool may share.\n#\n# A counter that every core adds to is an `Atomic`; an `Array` that every\n# core appends to sits behind a `Mutex`. Both are `Share`, so a parallel\n# block may capture them.\nhits = Atomic(Int64).new(0_i64)\npaths = Mutex.new { [] of String }\n\n# 3. Module state a call reaches.\n#\n# A parallel block that names `GREETING` is refused, because an `Array` is\n# not `Share`. One that only calls `greeting` used to compile with nothing\n# said; now the compiler warns at the block, naming the path. This constant\n# is set before the server starts and only read afterwards, so it says so.\n@[SafeReach("set once before the server starts, then only read")]\nGREETING = ["hello", "from", "every", "core"]\n\ndef greeting : String\n GREETING.join(" ")\nend\n\n# 4. A server on every core.\n#\n# `Server.serve` runs each connection on the pool's workers, so its block is\n# checked as `spawn_parallel`'s is. It stops through a channel: closing the\n# listener from inside the block would capture it, and `/stop` may arrive on\n# any worker. `workers: 1` keeps the one-thread server, whose block may\n# capture anything.\nlistener = IyiSocket.listen("127.0.0.1", 0)\nbase = "http://127.0.0.1:#{listener.local_port}"\nstop = Channel(Int32).new(1)\ngroup do |g|\n g.spawn do\n stop.receive\n listener.close\n 0\n end\n g.spawn do\n Server.serve(listener) do |req|\n hits.add(1_i64)\n path = req.path\n paths.lock { |list| list << path }\n if path == "/stop"\n stop.send(0)\n Response.new(200, "bye")\n else\n Response.new(200, greeting)\n end\n end\n 0\n end\n g.spawn do\n puts HTTP.get(base + "/").or_panic.body # => hello from every core\n HTTP.get(base + "/stop").or_panic\n 0\n end\nend\nputs hits.get # => 2\npaths.lock { |list| puts list.sorted } # => ["/", "/stop"]\n", + "samples/iyi/cores.iyi": "# Work on every core - SPEC.md III.4.13, as a person writes it.\n#\n# ./bin/iyi run samples/iyi/cores.iyi\n#\n# `spawn` is a task the compiler places: on a pool of workers, on any core,\n# where it can prove that safe, and on the thread that spawned it otherwise\n# (III.4.15, III.4.16). `spawn(unit: :new)` asks for the pool, and its\n# block may capture only what is `Share` (III.4.4); `spawn(unit: :same)`\n# keeps the task on its spawner's thread whatever it captures. std/http's\n# `Server.serve` puts each connection on that pool, so its block is checked\n# the same way. When a block reaches module state through a call, the\n# compiler warns, and `@[SafeReach("why")]` on the state says why it is\n# safe - a claim the compiler proves, or refuses naming the write.\nmodule samples/cores\n\nimport std/http::{HTTP, Response, Server}\nimport std/socket::{IyiSocket}\nimport std/mutex::{Mutex}\n\ndef collatz_steps(n : Int64) : Int64\n steps = 0_i64\n while n != 1\n n = n % 2 == 0 ? n // 2 : 3 * n + 1\n steps += 1\n end\n steps\nend\n\n# 1. `spawn` and `spawn(unit: :new)`.\n#\n# These `spawn`s append to an `Array` they captured, which is not `Share`, so\n# the compiler keeps them on their spawner's thread, where they take turns\n# and nothing is raced; `iyi check` notes each one, with why. The tasks\n# `spawn(unit: :new)` starts run on whichever core is free; each answers\n# its own value, and a task's answer need not be `Share`, because it is\n# handed over once, after the task ends.\nseen = [] of Int64\ngroup do |g|\n (1..3).each do |n|\n g.spawn do\n seen << collatz_steps(n.to_i64)\n 0\n end\n end\nend\nputs seen.sorted # => [0, 1, 7]\n\nlongest = 0_i64\ngroup do |g|\n handles = [] of IyiTask(Int64)\n 4.times do |part|\n handles << g.spawn(unit: :new) do\n best = 0_i64\n start = part.to_i64 * 25_000 + 1\n (start..start + 24_999).each { |n| best = collatz_steps(n) if collatz_steps(n) > best }\n best\n end\n end\n handles.each do |handle|\n steps = handle.value\n longest = steps if steps.is_a?(Int64) && steps > longest\n end\nend\nputs longest # => 350, the longest chain below 100,000\n\n# 2. State the pool may share.\n#\n# A counter that every core adds to is an `Atomic`; an `Array` that every\n# core appends to sits behind a `Mutex`. Both are `Share`, so a parallel\n# block may capture them.\nhits = Atomic(Int64).new(0_i64)\npaths = Mutex.new { [] of String }\n\n# 3. Module state a call reaches.\n#\n# A parallel block that names `GREETING` is refused, because an `Array` is\n# not `Share`. One that only calls `greeting` used to compile with nothing\n# said; now the compiler warns at the block, naming the path. This constant\n# is set before the server starts and only read afterwards, so it says so,\n# and the compiler checks it: nothing writes `GREETING` once a parallel\n# task can start (SPEC.md III.4.4).\n@[SafeReach("set once before the server starts, then only read")]\nGREETING = ["hello", "from", "every", "core"]\n\ndef greeting : String\n GREETING.join(" ")\nend\n\n# 4. A server on every core.\n#\n# `Server.serve` runs each connection on the pool's workers, so its block is\n# checked as `spawn(unit: :new)`'s is. It stops through a channel: closing the\n# listener from inside the block would capture it, and `/stop` may arrive on\n# any worker. `workers: 1` keeps the one-thread server, whose block may\n# capture anything.\nlistener = IyiSocket.listen("127.0.0.1", 0)\nbase = "http://127.0.0.1:#{listener.local_port}"\nstop = Channel(Int32).new(1)\ngroup do |g|\n g.spawn do\n stop.receive\n listener.close\n 0\n end\n g.spawn do\n Server.serve(listener) do |req|\n hits.add(1_i64)\n path = req.path\n paths.lock { |list| list << path }\n if path == "/stop"\n stop.send(0)\n Response.new(200, "bye")\n else\n Response.new(200, greeting)\n end\n end\n 0\n end\n g.spawn do\n puts HTTP.get(base + "/").or_panic.body # => hello from every core\n HTTP.get(base + "/stop").or_panic\n 0\n end\nend\nputs hits.get # => 2\npaths.lock { |list| puts list.sorted } # => ["/", "/stop"]\n", "samples/iyi/derive.iyi": "# `derive`, and where it runs (SPEC.md R-5, II.4).\n#\n# ./bin/iyi run samples/iyi/derive.iyi\n#\n# A derive runs once, in the module that declares the type. What it generates is\n# an ordinary declaration of that module, so it travels in the module's\n# `.iyimod` and a consumer calls the derived method without running the macro\n# and without this module's source. `bench/samples_roundtrip.sh` builds this\n# sample from its artifacts with `std/derives` deleted, which is that claim\n# taken literally.\n#\n# The macro is handed the facts R-5 lets it read: the declaration's name, and\n# the fields written in its body, each with the type it was written as. It\n# cannot enumerate a type it was not given, which is what keeps a derive\n# bounded and cacheable.\n\nmodule samples/derive\n\nimport std/derives::*\n\npub struct Point\n @x : Int32\n @y : Int32\n\n def initialize(@x : Int32, @y : Int32)\n end\n\n derive described\nend\n\n# One derive, two declarations. The macro reads each one's own fields, so\n# neither type had to be written down inside the macro.\npub struct Span\n @from : Int32\n @to : Int32\n @label : String\n\n def initialize(@from : Int32, @to : Int32, @label : String)\n end\n\n derive described\nend\n\nputs Point.new(3, 4).described\nputs Span.new(1, 9, "range").described\n\n# The field type is a type, not a name, so a derive can ask what it implements.\n# `Name` and `Show` both belong to `std/derives`, and both arrive from that\n# module's artifact when its source is gone: `@label` is rendered through the\n# trait, and `@count` is skipped because `Int32` does not implement it.\n#\n# `getter label : Name` is a macro call, so the field it declares exists only\n# once it has run. A derive reads the declarations above it, which is why\n# `derive` goes last. Below `getter`, this would be an error rather than a\n# method generated over no fields.\n#\n# One line, two derives: each macro reads the same declaration and adds its own\n# methods.\npub struct Tag\n getter label : Name\n @count : Int32\n\n def initialize(@label : Name, @count : Int32)\n end\n\n derive described, shown\nend\n\nputs Tag.new(Name.new("hi"), 2).described\nputs Tag.new(Name.new("hi"), 2).shown\n\n# ---------------------------------------------------------------------------\n# What this sample would report as an error\n# ---------------------------------------------------------------------------\n#\n# * `derive missing` where nothing exports a `missing` macro. The error names\n# the macro and the declaration it would have to be written as, rather than\n# failing as an undefined method on the type's metaclass.\n#\n# * `derive` outside a class or struct body. There is no declaration to read,\n# so there is nothing for the macro to be handed.\n#\n# * A `derive` inside a reopened type from another module. That is refused\n# before the derive is reached, by R-3: iyi has no open classes, so a module\n# cannot add to a type it does not declare.\n#\n# * A `getter` (or any macro call that declares a field) written below the\n# `derive` that should read it. The derive names the call and says which way\n# to move, rather than generating a method over the fields it happened to see.\n", "samples/iyi/enums.iyi": "# An `enum`, used the way a program uses one — SPEC.md IV.2.\n#\n# ./bin/iyi run samples/iyi/enums.iyi\n#\n# The compiler gives an enum two methods: `value`, the integer the member was\n# numbered with, and `new`, which wraps one. Everything below is the prelude's\n# (`src/iyi/enum.iyi`), and until this sample asked for it none of it was\n# there: `Level::Warn == Level::Warn` was false, `"#{Level::Warn}"` printed\n# `Samples::Enums::Level`, and a `case` over the members matched nothing —\n# because a `when` that names a member lowers to `Level::Warn === level`, and\n# `===` is `==`.\n#\n# Two enums, because the compiler writes different code for each: a plain one\n# whose `case` the exhaustiveness checker holds to every member, and a\n# `@[Flags]` one, where it synthesizes `None`, `All` and question methods that\n# ask `includes?`.\n\nmodule samples/enums\n\n# A level is an order as well as a name, which is why the threshold below\n# works: `Warn >= Info` is the whole reason a program writes one.\nenum Level : Int32\n Debug = 0\n Info = 1\n Warn = 2\n Error = 3\nend\n\n# A set of bits, one per member, which is what `@[Flags]` numbers them as.\n@[Flags]\nenum Mode\n Read\n Write\n Append\nend\n\n# 1. A member prints as its name, in interpolation and out of `to_s`.\nlevel = Level::Warn\nputs "level: #{level}"\nputs "spelled: " + Level::Error.to_s\n\n# 2. Equality, and the `case` that is built on it — a `when` naming a member\n# lowers to `===`, which is `==`.\nputs "same: #{level == Level::Warn}"\nputs "other: #{level == Level::Debug}"\n\n# `in` rather than `when`, which is the form the exhaustiveness checker holds\n# to every member: the day a member is added this sample stops compiling\n# rather than falling through, and the answer is a `String` rather than a\n# `String | Nil` because there is nothing left for an `else` to catch.\nverdict = case level\n in Level::Debug then "quiet"\n in Level::Info then "ordinary"\n in Level::Warn then "loud"\n in Level::Error then "loudest"\n end\nputs "verdict: #{verdict}"\n\n# 3. The question methods the compiler writes, which call the prelude's `==`.\nputs "warn? #{level.warn?}"\nputs "error? #{level.error?}"\n\n# 4. An order, so a level can be a threshold.\nputs "at least info: #{level >= Level::Info}"\nputs "below error: #{level < Level::Error}"\n\n# 5. The members, as a list — a menu, a validation table, a loop over all of\n# them. `names` is the same list said in text.\nLevel.values.each do |candidate|\n print candidate.to_s\n print " "\nend\nputs ""\nputs "names: #{Level.names.size}"\n\n# 6. A name read back, which is what a program does with an argument or a\n# configuration line. Unknown text answers `nil` rather than a panic, and the\n# lenient form takes the spelling a person types.\nputs "parsed: #{Level.parse?("Error").to_s}"\nputs "lenient: #{Level.parse?("warn").to_s}"\nputs "unknown: #{Level.parse?("shout").inspect}"\n\n# 7. The value, for a wire format or a C call, and the way back.\nputs "value: #{Level::Error.value}"\nputs "from 1: #{Level.from_value?(1).to_s}"\nputs "from 9: #{Level.from_value?(9).inspect}"\n\n# 8. A flags enum: the two set operators, membership, and a `to_s` that says\n# which bits are in it.\nmode = Mode::Read | Mode::Write\nputs "mode: #{mode}"\nputs "read: #{mode.includes?(Mode::Read)}"\nputs "append: #{mode.append?}"\nputs "none: #{Mode::None.to_s}"\nputs "masked: #{(mode & Mode::Write).to_s}"\n\n# 9. As a `Hash` key, which needs `hash` as well as `==`.\ncounts = {} of Level => Int32\ncounts[Level::Warn] = 2\ncounts[Level::Error] = 1\nputs "warns: #{counts[Level::Warn]}"\nputs "errors: #{counts[Level::Error]}"\n", "samples/iyi/errors.iyi": "# Errors are ordinary union members — SPEC.md III.1.\n#\n# ./bin/iyi run samples/iyi/errors.iyi\n#\n# No `Result` wrapper, no exception hierarchy, no new type machinery: unions\n# already exist and already carry a type id. What makes a member an *error*\n# member is that its type implements `Error`, a trait the compiler owns.\n\nmodule samples/errors\n\npub struct IOError\n getter path : String\n\n def initialize(@path : String)\n end\nend\n\n# `Error` is not declared here. It is the compiler's, so that `!` can ask\n# whether a member implements it without a name in scope deciding the answer.\nimpl Error for IOError\n def message : String\n "no such file: #{path}"\n end\nend\n\npub struct ParseError\n getter text : String\n\n def initialize(@text : String)\n end\nend\n\nimpl Error for ParseError\n def message : String\n "not a number: #{text}"\n end\nend\n\npub def read(path : String) : String | IOError\n return IOError.new(path) if path == "missing"\n path == "bad" ? "twelve" : "12"\nend\n\npub def to_number(text : String) : Int32 | ParseError\n return ParseError.new(text) unless text == "12"\n 12\nend\n\n# `!` propagates. If the value is a non-error member the expression evaluates to\n# it — narrowed, so `to_number` sees a `String` and not the union. If it is an\n# error member it is returned from here, which is why `load` has to admit both\n# errors in its own return type. Nothing enforces that separately: it is the\n# ordinary return-type check on the return the operator writes.\npub def load(path : String) : Int32 | IOError | ParseError\n text = read(path)!\n to_number(text)!\nend\n\n# Handling needs nothing new. An exhaustive `case` already checks totality, so\n# adding an error member to `load` turns every incomplete `case` on it into a\n# compile error. That is the main ergonomic argument for the whole approach and\n# it cost nothing to build.\n["config", "missing", "bad"].each do |path|\n case load(path)\n in Int32 then puts "read #{it}"\n in IOError then puts "io: #{it.message}"\n in ParseError then puts "parse: #{it.message}"\n end\nend\n\n# `it` is the value being matched, and it is narrowed per branch: the first sees\n# an `Int32`, the other two see one error type each, which is how `.message`\n# resolves without anything asking what the union holds. It is an assignment the\n# compiler writes into each branch, not new machinery — so `it` outlives the\n# `case` the way any variable assigned inside an `if` does, and a nested `case`\n# shadows it.\n\n# Where a `case` is overkill, `.or` takes a default and `.or_panic` takes the\n# value or dies (III.1.3). These cannot be ordinary methods: by II.1 a real\n# method call on `Int32 | IOError` would demand it from *both* members, and a\n# method every error type has to supply is what this design avoids. So the\n# compiler knows them by name — which is also why `or` and `or_panic` are not\n# names an iyi program can use for anything else.\n["config", "missing", "bad"].each do |path|\n puts "or: #{load(path).or(0)}"\nend\n\n# `.or` is an `is_a?(::Error)` and nothing more, so the result type falls out\n# instead of being computed: the default's type unioned with the non-error\n# members. Here that is `Int32`, and the default is only evaluated when there is\n# an error to recover from.\nputs "or: #{load("missing").or(-1)}"\n\n# `.or_panic` yields the non-error members alone, because what it calls never\n# returns. It reaches `message` on a value narrowed to `IOError | ParseError` —\n# two unrelated types — which resolves by II.1: both implement `Error`, so their\n# union does. Until panics exist (III.1.4), this unwinds as a Crystal exception.\nputs "panic: #{load("config").or_panic}"\n\n# ---------------------------------------------------------------------------\n# defer — III.1.4\n# ---------------------------------------------------------------------------\n#\n# Once errors are values returned early, the interesting exit from a body is an\n# ordinary `return` in the middle of it, and `begin`/`ensure` makes you wrap\n# everything after the acquisition to cover it. `defer` names the cleanup at the\n# acquisition instead. It expands to that same `ensure`, so it runs on a normal\n# exit, on a `!` propagation, and on an unwind — no new runtime machinery, only\n# a different shape.\npub def session(path : String) : Int32 | IOError | ParseError\n puts " open outer"\n defer puts " close outer"\n\n text = read(path)!\n\n puts " open inner"\n defer puts " close inner"\n\n to_number(text)!\nend\n\n# Cleanup is LIFO, and that is the nesting rather than a rule: the second\n# `defer` expands inside the first one's body, so its `ensure` is the inner one.\n# The failing run proves the part that matters — `read(path)!` returns from the\n# middle of the body and the cleanup still happens.\nputs "success:"\nputs session("config").or(-1)\nputs "propagating:"\nputs session("missing").or(-1)\n\n# The scope is the block, not the function. This is the departure from Go, and\n# it is what the expansion does rather than an extra rule: the cleanup lands\n# inside the loop body, so it runs each iteration instead of piling up until the\n# function returns — Go's best-known wart with the feature.\n2.times do |i|\n defer puts " iteration #{i} closed"\n puts " iteration #{i}"\nend\n\n# ---------------------------------------------------------------------------\n# What this sample would report as an error\n# ---------------------------------------------------------------------------\n#\n# * Dropping the `in ParseError` branch — `case is not exhaustive`.\n#\n# * `def go : String` with `read(p)!` in it — the error names the return type\n# and the error being propagated into it.\n#\n# * `!` on something with no error member, such as an `Int32?`. Rejected rather\n# than given a surprising meaning: `Nil` does not implement `Error` (III.1.5),\n# so without the check this would compile and silently do nothing.\n#\n# * `!` on a value whose every member is an error. It could never produce one,\n# and a function that genuinely never succeeds returns `NoReturn`.\n#\n# * `def sort!` — `!` is not part of a name (III.1.7). This is the one place\n# that still explains the naming convention, because after a `def` name there\n# is nothing to propagate.\n#\n# * `.or` and `.or_panic` on the same two operands `!` rejects. With no error\n# member there is nothing to recover; with every member an error, `.or` can\n# only return its default and `.or_panic` can only panic.\n#\n# * `.or 0` without parentheses — `.or` takes the default in parentheses, so\n# the shape cannot be confused with a call to a method of that name.\n#\n# * `it` in a `case` over a tuple, such as `case {a, b}` — there is no single\n# value to name, so `it` stays undefined rather than being bound to one of\n# the elements.\n#\n# * `defer close(f)!` — a `defer` runs while the scope is already being left,\n# possibly carrying an error of its own, so a second one leaving from there\n# is the error-during-error problem. Handle it in the `defer` body.\n#\n# * `impl Error for String` here — the orphan rule (R-3). `Error` is the\n# compiler's and `String` is the prelude's, so neither belongs to a module\n# this impl could be at home in, and every module that can name both would be\n# free to write its own.\n#\n# ---------------------------------------------------------------------------\n# Not yet implemented\n# ---------------------------------------------------------------------------\n#\n# * Panics (III.1.4). They are meant to be catchable only at task boundaries,\n# and what a task boundary is belongs to Part V.5. Until then `.or_panic`\n# raises a Crystal exception, and `defer` runs on that unwind either way.\n", diff --git a/records/stdlib.json b/records/stdlib.json index 74348df..d7d9124 100644 --- a/records/stdlib.json +++ b/records/stdlib.json @@ -1,22 +1,22 @@ { "recorded": { - "compiler": "iyi 0.20.1 (built on Crystal 1.22.0-dev [91d012b1e] (2026-11-04))", - "commit": "91d012b1ed978c7df5cc033624ec161a2daf01b6", + "compiler": "iyi 0.21.0 (built on Crystal 1.22.0-dev [08ce6c1cb] (2026-11-09))", + "commit": "08ce6c1cb2ba5768b15485dbbd6413fcb17df19a", "machine": "Linux 7.0.0-38-generic x64, QEMU Virtual CPU version 2.5+", "command": "npm run record:stdlib", - "when": "2026-11-04T04:32:59.606Z" + "when": "2026-11-09T17:40:51.224Z" }, "modules": [ { "name": "std/acme", "path": "src/std/acme.iyi", - "sourceSha256": "3182946f098dcd1fcc2e03165324b06abc43d96ddd45a34d8b13c36df821ed19", + "sourceSha256": "5eee5cf4cef59fa1c1dd1cacecc58bc6806a3722e85d3778d43301cc68b24e90", "surface": "module std/acme\n\n# An authorization (section 7.1.4): the identifier, its status, and the\n# challenges by which it can be proved.\npub struct AcmeAuthorization\n # The challenge of *kind* offered, or why there is none.\n def challenge(kind : String) : AcmeChallenge | AcmeError\n def challenges : Array(AcmeChallenge)\n def self.from(url : String, object : Hash(String, Any)) : AcmeAuthorization | AcmeError\n def identifier : String\n def initialize(url : String, identifier : String, status : String, wildcard : Bool, challenges : Array(AcmeChallenge))\n def status : String\n def url : String\n def wildcard : Bool\nend\n\n# A challenge (section 8): its type (`http-01`, `dns-01`, `tls-alpn-01`),\n# where to answer it, its token, and its status, with the problem that\n# made it invalid.\npub struct AcmeChallenge\n def error : AcmeError | ::Nil\n def self.from(object : Hash(String, Any)) : AcmeChallenge | AcmeError\n def initialize(kind : String, url : String, token : String, status : String, error : AcmeError | ::Nil)\n def kind : String\n def status : String\n def token : String\n def url : String\nend\n\n# A session with an ACME server under one account key.\npub class AcmeClient\n protected def account=(url : String | ::Nil) : String | ::Nil\n # The account's URL, the `kid` every request after newAccount carries.\n def account_url : String | ::Nil\n # The authorization at *url*, read (POST-as-GET).\n def authorization(url : String) : AcmeAuthorization | AcmeError\n # `badNonce` answers sent again, over this session.\n def bad_nonce_retries : Int32\n # The certificate chain of a valid *order*, PEM, leaf first (section\n # 7.4.2): every block std/x509 reads, or why one does not read.\n def certificate(order : AcmeOrder) : String | AcmeError\n # How long to wait, in milliseconds, before polling again, as\n # *retry_after* - a Retry-After value - says (RFC 9110 section 10.2.3:\n # seconds, or an HTTP-date, judged against *now*), or *fallback_ms*\n # when there is none or it is neither. A date already past is no wait.\n def self.delay(retry_after : String | ::Nil, fallback_ms : Int32, now : Time) : Int64\n def directory : AcmeDirectory\n # The name dns-01's TXT record is put under for *domain*: a wildcard's\n # base domain, with `_acme-challenge.` before it.\n def self.dns_name(domain : String) : String\n # The dns-01 TXT record's value for *token* (section 8.4).\n def dns_txt(token : String) : String\n # One HTTP exchange, its answer read whole up to `MAX_BODY`; nothing\n # sent to a URL that is not `https://`.\n protected def self.exchange(method : String, url : String, body : String, accept : String | ::Nil, timeout_ms : Int32) : AcmeResponse | AcmeError\n # The order finalized with a certificate request for its names that\n # *key* signs (section 7.4): polled until its authorizations make it\n # ready, finalized, and polled until it is valid, with the certificate's\n # URL; or the error that made it invalid.\n def finalize(order : AcmeOrder, key : SigningKey) : AcmeOrder | AcmeError\n # Nil when *url* is an `https://` URL with a host, else the error that\n # refuses it as *what*: RFC 8555 section 6.1 requires HTTPS of every\n # ACME request, and anything else is plaintext an attacker on the path\n # can answer as the CA.\n def self.https(url : String, what : String) : AcmeError | ::Nil\n def initialize(directory_url : String, key : SigningKey, directory : AcmeDirectory, timeout_ms : Int32)\n def key : SigningKey\n # *token*'s key authorization (section 8.1): `token.thumbprint`.\n def key_authorization(token : String) : String\n # The most polls one wait makes, and the most milliseconds it takes.\n def max_polls : Int32\n # The most polls one wait makes, and the most milliseconds it takes.\n def max_polls=(value : Int32) : Int32\n # A session with the server whose directory is at *url*, signing with\n # *key*: the directory read, or why it could not be. *account_url*, an\n # account `register` answered before, is the `kid` from the start, so a\n # session that kept it need not register again. Both are `https://`.\n def self.new(url : String, key : SigningKey, timeout_ms : Int32 = 30000, account_url : String | ::Nil = nil) : AcmeClient | AcmeError\n # A new order for *names*, DNS identifiers (section 7.4).\n def new_order(names : Array(String)) : AcmeOrder | AcmeError\n # The order at *url*, read (POST-as-GET).\n def order(url : String) : AcmeOrder | AcmeError\n # How long to wait between polls when an answer has no Retry-After.\n def poll_ms : Int32\n # How long to wait between polls when an answer has no Retry-After.\n def poll_ms=(value : Int32) : Int32\n # The account for this key, made if there is none (section 7.3) - with\n # *contact*'s URLs (`mailto:`), the terms agreed to when\n # *terms_agreed* - or only found when *only_existing*: its URL, which\n # every request after this carries as `kid`.\n def register(contact : Array(String) = [] of String, terms_agreed : Bool = false, only_existing : Bool = false) : String | AcmeError\n # Tells the server *challenge* is ready to be checked (section 7.5.1):\n # its URL posted `{}`. Put the answer in place first.\n def respond(challenge : AcmeChallenge) : AcmeChallenge | AcmeError\n # The account moved to *new_key* (section 7.3.5): keyChange's inner JWS\n # signed by the new key, carrying its `jwk`, inside one the old key\n # signs as the account.\n def roll_key(new_key : SigningKey) : Nil | AcmeError\n # The account key's JWK thumbprint (RFC 7638), SHA-256 and base64url.\n def thumbprint : String\n # Each HTTP exchange's connect, and each read and write in it.\n def timeout_ms : Int32\n # Each HTTP exchange's connect, and each read and write in it.\n def timeout_ms=(value : Int32) : Int32\n # The authorization at *url* polled until it is no longer pending: the\n # valid authorization, or the error that made it something else.\n def wait_authorization(url : String) : AcmeAuthorization | AcmeError\n def wait_ms : Int32\n def wait_ms=(value : Int32) : Int32\nend\n\n# The directory's URLs (section 7.1.1).\npub struct AcmeDirectory\n def external_account_required : Bool\n def initialize(new_nonce : String, new_account : String, new_order : String, key_change : String | ::Nil, revoke_cert : String | ::Nil, terms_of_service : String | ::Nil, external_account_required : Bool)\n def key_change : String | ::Nil\n def new_account : String\n def new_nonce : String\n def new_order : String\n def self.parse(text : String) : AcmeDirectory | AcmeError\n def revoke_cert : String | ::Nil\n def terms_of_service : String | ::Nil\nend\n\n# What a request answered that it should not have, or could not do: a\n# problem document's `type` and `detail` (RFC 7807), or, with\n# `problem_type` empty, what failed beneath ACME.\npub struct AcmeError\n def detail : String\n # The identifier a subproblem is about (section 6.7.1), \"\" otherwise.\n def identifier : String\n def initialize(detail : String, problem_type : String = \"\", status : Int32 = 0, identifier : String = \"\", subproblems : Array(AcmeError) = [] of AcmeError)\n # Whether this came from a problem document.\n def problem? : Bool\n def problem_type : String\n # The HTTP status the problem came with; 0 when there was no answer.\n def status : Int32\n def subproblems : Array(AcmeError)\n def to_s : String\nend\n\n# http-01's answers (section 8.3), by token, for a std/http server: GET\n# `/.well-known/acme-challenge/` is answered with the token's key\n# authorization, a token not held with 404, any other path with nil - the\n# server's own to answer.\n#\n# responder = AcmeHttp01.new\n# Server.serve(listener, workers: 1) { |request| responder.answer(request) || app(request) }\n#\n# The table of answers is written as orders run, so it is one thread's:\n# the server that answers from it serves on that thread (`workers: 1`).\npub class AcmeHttp01\n def add(token : String, key_authorization : String) : Nil\n def answer(request : Request) : Response | ::Nil\n def initialize\n def remove(token : String) : Nil\nend\n\n# JWS (RFC 7515) as ACME signs it: the flattened JSON serialization, the\n# account's key as RFC 7638's canonical JWK, and the signature in the\n# form JWA (RFC 7518) gives each algorithm.\npub struct AcmeJws\n # The JWS algorithm *key* signs under: ES256, ES384, RS256 or EdDSA.\n def self.alg(key : SigningKey) : String\n # *data* in base64url without padding (RFC 7515 section 2).\n def self.b64u(data : String | Array(UInt8)) : String\n def self.bytes(text : String) : Array(UInt8)\n # The protected header: `alg`, then `jwk` when *kid* is nil and `kid`\n # when it is not, `nonce` when there is one, and `url`.\n def self.header(key : SigningKey, nonce : String | ::Nil, url : String, kid : String | ::Nil) : String\n def initialize\n # *key* as a JWK holding its required members alone, sorted, without\n # whitespace (RFC 7638 section 3): the text its thumbprint is taken of,\n # and the `jwk` a request carries.\n def self.jwk(key : RsaPublicKey | EcdsaPublicKey | Ed25519PublicKey) : String\n # A big-endian number without its leading zero bytes, as JWA writes\n # one (RFC 7518 section 6.3.1).\n def self.minimal(bytes : Array(UInt8)) : Array(UInt8)\n # An Ecdsa-Sig-Value's DER as R || S, each *size* bytes.\n def self.raw(der : Array(UInt8), size : Int32) : Array(UInt8)\n # The flattened JSON serialization (RFC 7515 section 7.2.2) of *payload*\n # - nil for the empty payload of POST-as-GET - under *header*.\n def self.sign(key : SigningKey, header : String, payload : String | ::Nil) : String\n # *key*'s JWS signature of *input*: R || S for ECDSA, each at the\n # curve's width (RFC 7518 section 3.4), where std/p256 and std/p384 sign\n # in X.509's DER; RSA's and Ed25519's as they are.\n def self.signature(key : SigningKey, input : Array(UInt8)) : Array(UInt8)\n # *key*'s JWK thumbprint (RFC 7638): base64url of the SHA-256 of `jwk`.\n def self.thumbprint(key : RsaPublicKey | EcdsaPublicKey | Ed25519PublicKey) : String\nend\n\n# An order (section 7.1.3): its URL, status, names, the authorizations to\n# prove, where to finalize it, and, once it is valid, its certificate.\npub struct AcmeOrder\n def authorizations : Array(String)\n def certificate_url : String | ::Nil\n def error : AcmeError | ::Nil\n def finalize_url : String\n def self.from(url : String, object : Hash(String, Any)) : AcmeOrder | AcmeError\n def identifiers : Array(String)\n def initialize(url : String, status : String, identifiers : Array(String), authorizations : Array(String), finalize_url : String, certificate_url : String | ::Nil, error : AcmeError | ::Nil)\n def status : String\n def url : String\nend\n\n# tls-alpn-01 (RFC 8737): the certificate a TLS server shows to a client\n# whose ALPN offers `acme-tls/1`, and a std/tls config that shows it.\npub struct AcmeTlsAlpn01\n # The self-signed certificate for *domain* (section 3): its one dNSName\n # *domain*, and the critical acmeIdentifier extension, an OCTET STRING\n # of the SHA-256 of *key_authorization*; signed by *key*, valid from a\n # day before *now* to a week after. A *domain* past 64 characters is\n # refused, as std/x509 refuses its common name.\n def self.certificate(domain : String, key_authorization : String, key : SigningKey, now : Time = Time.utc) : Array(UInt8) | AcmeError\n # A std/tls server config that shows `certificate` for *domain*, under a\n # P-256 key made for it, and speaks `acme-tls/1` alone by ALPN.\n def self.config(domain : String, key_authorization : String) : TlsServerConfig | AcmeError\n def initialize\nend\n\nimpl Error for AcmeError\n def message : String\nend\n" }, { "name": "std/aead", "path": "src/std/aead.iyi", - "sourceSha256": "6b1bdaac65733c8d4b3cd6630ddc53535b7196378b048ebdaf7dcea1f75535a1", + "sourceSha256": "78766eb35ab70429a50e37ca813f2c84f7702cc92f36402ac55d8340ac79cca8", "surface": "module std/aead\n\n# The AES block cipher, FIPS 197: a 16, 24 or 32-byte key (10, 12 or 14\n# rounds), one 16-byte block at a time, either way. On its own it is a\n# building block; a message wants a mode, `AesGcm`.\npub class AES\n # FIPS 197's InvCipher: the block `encrypt_block` answered, back.\n def decrypt_block(block : String | Array(UInt8)) : String\n # FIPS 197's Cipher: 16 bytes in, 16 out.\n def encrypt_block(block : String | Array(UInt8)) : String\n def initialize(key : String | Array(UInt8))\nend\n\n# What `open` answers for sealed bytes it will not open: shorter than a\n# tag, or whose tag does not match.\npub struct AeadError\n def initialize(message : String)\n def to_s : String\nend\n\n# AES in Galois/Counter Mode, NIST SP 800-38D: CTR encryption from the\n# counter after the nonce's own, and a 128-bit tag that is GHASH over the\n# additional data and the ciphertext, each padded to a block, and their\n# lengths in bits, XORed with the key stream of the nonce's counter\n# block. The nonce is 12 bytes, and the counter block is it and a 32-bit\n# counter that starts at 1 (§7.1).\npub class AesGcm\n def initialize(key : String | Array(UInt8))\n # The plaintext *sealed* came from, when its tag is the one this key,\n # *nonce* and *aad* give; else an `AeadError`, and nothing decrypted.\n def open(nonce : String | Array(UInt8), sealed : String | Array(UInt8), aad : String | Array(UInt8) = \"\") : String | AeadError\n # The ciphertext of *plaintext*, as long as it, and the 16-byte tag.\n def seal(nonce : String | Array(UInt8), plaintext : String | Array(UInt8), aad : String | Array(UInt8) = \"\") : String\nend\n\n# The ChaCha20 stream cipher, RFC 8439 §2.4: a 32-byte key, a 12-byte\n# nonce and a 32-bit block counter, which starts at *counter*. `xor`\n# answers its data XORed with the key stream and carries on from where\n# it stopped, so a message XORed in pieces is the message XORed whole;\n# the same call decrypts. The counter is the RFC's 32 bits, 256 GiB of\n# key stream; a stream run past block 2^32 - 1 is refused rather than\n# wrapped onto key stream already used.\npub class ChaCha20\n def initialize(key : String | Array(UInt8), nonce : String | Array(UInt8), counter : UInt32 = 0_u32)\n # *count* bytes from *src* to *dst*, which may be the same bytes.\n def xor(src : Pointer(UInt8), dst : Pointer(UInt8), count : Int32) : Nil\n def xor(data : String | Array(UInt8)) : String\nend\n\n# ChaCha20 and Poly1305 as an AEAD, RFC 8439 §2.8: the Poly1305 key is\n# the first 32 bytes of block 0's key stream, the plaintext is encrypted\n# from block 1, and the tag covers the additional data and the\n# ciphertext, each padded with zeros to 16 bytes, then their lengths as\n# 64-bit little-endian numbers.\npub class ChaCha20Poly1305\n def initialize(key : String | Array(UInt8))\n # The plaintext *sealed* came from, when its tag is the one this key,\n # *nonce* and *aad* give; else an `AeadError`, and nothing decrypted.\n def open(nonce : String | Array(UInt8), sealed : String | Array(UInt8), aad : String | Array(UInt8) = \"\") : String | AeadError\n # The ciphertext of *plaintext*, as long as it, and the 16-byte tag.\n def seal(nonce : String | Array(UInt8), plaintext : String | Array(UInt8), aad : String | Array(UInt8) = \"\") : String\nend\n\n# Poly1305, RFC 8439 §2.5: a 16-byte MAC under a 32-byte one-time key, r\n# (clamped) and s. One key authenticates one message: a second message\n# under it lets whoever sees both tags forge a third, which is why the\n# AEAD derives a fresh key from each nonce, and why there is no `reset`.\n# `Poly1305.mac(key, data)` is one piece; `new`, `update` as the message\n# arrives, `final` once is the same in pieces. A tag someone sent is\n# compared with std/crypto's `Subtle.constant_time_compare`, not `==`.\npub class Poly1305\n # The 16-byte tag. A last block shorter than 16 bytes carries its own\n # 1 bit after its bytes (§2.5.1), rather than the 2^128 of a whole one.\n def final : String\n def initialize(key : String | Array(UInt8))\n def self.mac(key : String | Array(UInt8), data : String | Array(UInt8)) : String\n def update(data : Pointer(UInt8), count : Int32) : self\n def update(data : String | Array(UInt8)) : self\nend\n\nimpl Error for AeadError\n def message : String\nend\n" }, { @@ -28,31 +28,31 @@ { "name": "std/asn1", "path": "src/std/asn1.iyi", - "sourceSha256": "d8a5c75bdde73037027287b62ecc89ffc83595fdee77713cfce3d451f46645e4", + "sourceSha256": "24f026d27ed4b5cd126acdd1cab59ba4a44c9fc97e0125df8794d4d3f71a3cc4", "surface": "module std/asn1\n\n# What `Der.parse`, `children` and the typed readers answer for bytes that\n# are not the DER they were asked for: a sentence, and the offset in the\n# bytes `Der.parse` was given where the trouble is.\npub struct Asn1Error\n def initialize(message : String, offset : Int32)\n def offset : Int32\n def to_s : String\nend\n\npub struct Der\n def initialize\n # The one value *bytes* hold, and nothing after it.\n def self.parse(bytes : Array(UInt8)) : DerValue | Asn1Error\n # Every value under *value*, *value* first, in the order they are\n # written, each with its depth below it: the whole tree read and every\n # length and tag checked. Primitive contents are not decoded; that is\n # the typed readers' job, asked of the values a schema names.\n def self.walk(value : DerValue, & : (DerValue, Int32 -> Nil)) : Nil | Asn1Error\nend\n\n# The two bits at the top of an identifier byte.\npub enum DerClass : Int32\n Universal = 0\n Application = 1\n ContextSpecific = 2\n Private = 3\nend\n\n# The universal tag numbers this module reads (X.680 section 8.6).\npub struct DerTag\n def initialize\nend\n\n# One value: its identifier, and where its header and content are in the\n# bytes it was read from.\npub struct DerValue\n # A BIT STRING whose bits fill whole bytes: a key, a signature.\n def bit_string : Array(UInt8) | Asn1Error\n # A BIT STRING's bytes and how many bits at the end of the last are not\n # part of it, which DER sets to zero: a flags field such as keyUsage.\n def bit_string_bits : ::Tuple(Array(UInt8), Int32) | Asn1Error\n def boolean : Bool | Asn1Error\n # The values a constructed value holds, read one level down.\n def children : Array(DerValue) | Asn1Error\n def constructed? : Bool\n # The content bytes, copied.\n def content : Array(UInt8)\n # The offset of the first content byte, and of the byte after the value.\n def content_offset : Int32\n def content_size : Int32\n def context?(tag : Int32) : Bool\n # The identifier, said the way an error names it: `INTEGER`,\n # `[0] constructed`, `APPLICATION 3`.\n def describe : String\n def end_offset : Int32\n # An ENUMERATED that fits an Int64: a CRL entry's reason code, an\n # OCSP response's status.\n def enumerated : Int64 | Asn1Error\n # An ENUMERATED's two's-complement bytes, held to INTEGER's rules.\n def enumerated_bytes : Array(UInt8) | Asn1Error\n # The one value inside an explicit context tag `[n]`, as X.509 writes a\n # certificate's version and extensions.\n def explicit(tag : Int32) : DerValue | Asn1Error\n def header_size : Int32\n def initialize(data : Array(UInt8), tag_class : DerClass, constructed : Bool, tag : Int32, offset : Int32, header_size : Int32, content_size : Int32)\n # An INTEGER that fits an Int64.\n def integer : Int64 | Asn1Error\n # An INTEGER's two's-complement bytes, as written: checked to be the\n # fewest that hold it. Only an INTEGER: an ENUMERATED is written the same\n # way under another tag, and reading one here took an Ecdsa-Sig-Value\n # whose r or s was tagged ENUMERATED - a signature no DER reader accepts.\n def integer_bytes : Array(UInt8) | Asn1Error\n def null : Nil | Asn1Error\n def octet_string : Array(UInt8) | Asn1Error\n def offset : Int32\n # An OBJECT IDENTIFIER in dotted form, `\"2.5.4.3\"`. A subidentifier past\n # an Int64 is refused: no algorithm or attribute is one.\n def oid : String | Asn1Error\n # The whole value as it was written, header included: what a signature\n # over a certificate's `tbsCertificate` is computed on.\n def raw : Array(UInt8)\n # One value's header at *at*, its content within *stop*.\n def self.read(data : Array(UInt8), at : Int32, stop : Int32) : DerValue | Asn1Error\n # A SEQUENCE's elements: `children`, after checking it is one.\n def sequence : Array(DerValue) | Asn1Error\n # A SET's elements.\n def set : Array(DerValue) | Asn1Error\n # Any of the string types X.509 names are written in, as UTF-8.\n def string : String | Asn1Error\n def tag : Int32\n def tag_class : DerClass\n # A UTCTime or a GeneralizedTime, in the one form DER writes each.\n def time : Time | Asn1Error\n def universal?(tag : Int32) : Bool\n def self.universal_name(tag : Int32) : String\n # A non-negative INTEGER's magnitude, big-endian, without the 0x00 a\n # high bit makes DER write: an RSA modulus, a serial number. A negative\n # one is refused.\n def unsigned_integer : Array(UInt8) | Asn1Error\nend\n\nimpl Error for Asn1Error\n def message : String\nend\n" }, { "name": "std/atomic", "path": "src/std/atomic.iyi", - "sourceSha256": "3d1c544d3770dfa4b48136cda61ea466fc8ce6ea670fbe91b370f8ff21eda336", + "sourceSha256": "2fc9ae2d5bc23f8f76a6f2e043f29034020cf547069b913fb6b5ce9dc5a7a516", "surface": "module std/atomic\n\nstruct ::Atomic(T)\n def add_acq_rel(value : T) : T\n def add_acquire(value : T) : T\n def add_relaxed(value : T) : T\n def add_release(value : T) : T\n # `word &= value`; answers what the word held before.\n def and(value : T) : T\n # `word |= value`; answers what the word held before.\n def bitwise_or(value : T) : T\n def compare_and_set_acq_rel(expected : T, desired : T) : Tuple(T, Bool)\n def compare_and_set_acquire(expected : T, desired : T) : Tuple(T, Bool)\n # `compare_and_set` at each weaker ordering: if the word holds\n # `expected` it now holds `desired`; answers what the word held and\n # whether the exchange happened.\n def compare_and_set_relaxed(expected : T, desired : T) : Tuple(T, Bool)\n def compare_and_set_release(expected : T, desired : T) : Tuple(T, Bool)\n # A full fence: nothing crosses it in either direction.\n def self.fence : Nil\n # Both halves, without the total order of `fence`.\n def self.fence_acq_rel : Nil\n # Nothing after this fence moves before it.\n def self.fence_acquire : Nil\n # Nothing before this fence moves after it.\n def self.fence_release : Nil\n # The word, read once; nothing after the read moves before it.\n def get_acquire : T\n # The word, read once, atomic and nothing more.\n def get_relaxed : T\n # `word = max(word, value)`, signed or unsigned as `T` is; answers what\n # the word held before.\n def max(value : T) : T\n # `word = min(word, value)`, signed or unsigned as `T` is; answers what\n # the word held before.\n def min(value : T) : T\n # `word = ~(word & value)`; answers what the word held before.\n def nand(value : T) : T\n # The word, written once, atomic and nothing more.\n def set_relaxed(value : T) : Nil\n # The word, written once; nothing before the write moves after it.\n def set_release(value : T) : Nil\n def sub_acq_rel(value : T) : T\n def sub_acquire(value : T) : T\n def sub_relaxed(value : T) : T\n def sub_release(value : T) : T\n def swap_acq_rel(value : T) : T\n def swap_acquire(value : T) : T\n def swap_relaxed(value : T) : T\n def swap_release(value : T) : T\n # `word ^= value`; answers what the word held before.\n def xor(value : T) : T\nend\n\nstruct ::IyiAtomic\n def self.fence(ordering : Symbol, singlethread : Bool) : Nil\nend\n" }, { "name": "std/autocert", "path": "src/std/autocert.iyi", - "sourceSha256": "7342edc6a46dfb8bf30eaed23fc23d7b9fed3ecbd477ea88dadbe157a94e755e", + "sourceSha256": "7c3ad0f9bf39f109a6f834ed9bebbd5ea54ea5cabcbb5b3a91bd0602e763131c", "surface": "module std/autocert\n\n# Where a manager keeps its account key and its certificates: text by\n# key, nil for a key not held. Every value it is given holds a private\n# key.\npub trait AutocertCache\n abstract def delete(key : String) : AutocertError | ::Nil\n abstract def get(key : String) : String | AutocertError | Nil\n abstract def put(key : String, data : String) : AutocertError | ::Nil\nend\n\n# A cache in a directory: an entry a file named by its key. The directory\n# is made, 0700, the first time it is written to where it is not there;\n# an entry is written to a file of its own in it, created 0600 (the\n# umask may take more away, never less), then renamed over the old one.\npub class AutocertDirCache\n def dir : String\n def initialize(dir : String)\nend\n\n# Why a certificate could not be had, or the cache not read or written.\npub struct AutocertError\n def detail : String\n def initialize(detail : String)\n def to_s : String\nend\n\n# A certificate a manager holds for a name: the identity handshakes are\n# shown, its dates, and when renewal is next tried.\npub class AutocertHeld\n # Whether a handshake at *now_ms* may be shown it.\n def current?(now_ms : Int64) : Bool\n def failures : Int32\n def failures=(value : Int32) : Int32\n def identity : TlsIdentity\n def initialize(identity : TlsIdentity, renew_at_ms : Int64)\n def not_after_ms : Int64\n def not_before_ms : Int64\n # When renewal is next tried: its due time, later after failures.\n def renew_at_ms : Int64\n # When renewal is next tried: its due time, later after failures.\n def renew_at_ms=(value : Int64) : Int64\nend\n\n# Gets, keeps and renews certificates for a server's names from an ACME\n# CA, and shows them to std/tls handshakes by server_name.\npub class AutocertManager\n # Whether the CA's terms of service are agreed to when the account is\n # made: a CA that has terms refuses an account without.\n def accept_terms : Bool\n # Whether the CA's terms of service are agreed to when the account is\n # made: a CA that has terms refuses an account without.\n def accept_terms=(value : Bool) : Bool\n # Puts up the answers to *name*'s challenge: tls-alpn-01's certificate\n # for *key_authorization*, shown under `acme-tls/1`, and http-01's\n # answer for *token* (`answer`). An order puts them up while the CA\n # checks; a program answering a challenge of its own can too.\n def add_challenge(name : String, token : String, key_authorization : String) : AutocertError | ::Nil\n # http-01's answer to a request on port 80\n # (`/.well-known/acme-challenge/`), or nil for any other path:\n # `Server.serve(listener, workers: 1) { |r| manager.answer(r) || ... }`,\n # on one thread, since the manager's state is that thread's.\n def answer(request : Request) : Response | ::Nil\n def cache : AutocertCache\n # The certificate for *name*, normalized as `normalize` does: the one\n # held while it is current; else one the cache holds for it, or a new\n # one from the CA - by one order however many ask at once, waited for\n # *left_ms* at most when it is given - unless the host policy refuses\n # the name or its last order failed too recently.\n def certificate(name : String, left_ms : Int32 | ::Nil = nil) : AutocertHeld | AutocertError\n # The challenges offered, in the order tried.\n def challenges : Array(String)\n # The challenges offered, in the order tried.\n def challenges=(value : Array(String)) : Array(String)\n def directory_url : String\n # The account's contact address, without `mailto:`; none when empty.\n def email : String\n # The account's contact address, without `mailto:`; none when empty.\n def email=(value : String) : String\n # *config* - `tls_config`'s, or one the program got from it and set\n # more of, a `min_version` or `groups` - frozen for std/http's\n # `Server.serve(tls:)` on more than one worker: its identity source\n # runs on every worker, answers what this manager holds there, and\n # asks `run` for the rest (the header says how). `run` must run while\n # it serves.\n def frozen_tls_config(config : TlsServerConfig = tls_config) : TlsFrozenServerConfig\n # Whether a name may have a certificate; asked before the cache or the\n # CA. The names given to `new` and no other, unless replaced.\n def host_policy : Proc(String, Bool)\n # Whether a name may have a certificate; asked before the cache or the\n # CA. The names given to `new` and no other, unless replaced.\n def host_policy=(value : Proc(String, Bool)) : Proc(String, Bool)\n # The identity a handshake for *server_name*, offering *alpn*, is shown:\n # tls-alpn-01's certificate to a client offering `acme-tls/1` alone, the\n # name's certificate - got now if it must be, waiting *left_ms* at most\n # - to any other; or why there is none, as the error the handshake is\n # refused with: unrecognized_name for a name not answered for,\n # internal_error for one whose certificate could not be had in time.\n def identity_for(server_name : String | ::Nil, alpn : Array(String) | ::Nil, left_ms : Int32 | ::Nil = nil) : TlsIdentity | TlsError\n # A manager for the CA whose directory is at *directory_url*, keeping\n # its keys and certificates in *cache*, that answers for *hosts* alone.\n def initialize(directory_url : String, cache : AutocertCache, hosts : Array(String))\n # *host* as a server_name is compared: IDNA's A-labels for a Unicode\n # name, no trailing dot, lower case. A name IDNA refuses is compared as\n # it is written.\n def self.normalize(host : String) : String\n # Certificates ordered, and renewed, by this manager.\n def orders : Int32\n def remove_challenge(name : String, token : String) : Nil\n # How long before expiry a certificate is renewed.\n def renew_before : Span\n # How long before expiry a certificate is renewed.\n def renew_before=(value : Span) : Span\n # When a certificate valid from *not_before_ms* to *not_after_ms* is\n # due for renewal: `renew_before` ahead of its end, or two thirds into\n # a life no longer than `renew_before`.\n def renew_time(not_before_ms : Int64, not_after_ms : Int64) : Int64\n def renewals : Int32\n # How long a failed order is remembered, and a failed renewal put off;\n # doubled for each failure after, to `MAX_RETRY_MS`.\n def retry_ms : Int32\n # How long a failed order is remembered, and a failed renewal put off;\n # doubled for each failure after, to `MAX_RETRY_MS`.\n def retry_ms=(value : Int32) : Int32\n # The roots a chain the CA issues, or the cache holds, must reach to be\n # kept and shown: the system's when nil.\n def roots : Array(Certificate) | ::Nil\n # The roots a chain the CA issues, or the cache holds, must reach to be\n # kept and shown: the system's when nil.\n def roots=(value : Array(Certificate) | ::Nil) : Array(Certificate) | ::Nil\n # Renews certificates as they come due, until `stop`: run it in a task\n # of a group that lives as long as the server. Each held certificate is\n # looked at when it is due and whenever one is added; a renewal that\n # fails is tried again `retry_ms` later, doubling to an hour. While it\n # runs, the orders handshakes ask for run in its group too, so that one\n # outlives the handshake that asked; `stop` cancels those still running.\n # It answers a `frozen_tls_config`'s workers too, each ask in a task of\n # its group.\n def run : Nil\n # Ends `run`.\n def stop : Nil\n # A std/tls server config that shows this manager's certificates, and\n # speaks *alpn*'s protocols with `acme-tls/1` after them. Its own\n # `identity` is a self-signed certificate for `autocert.invalid` that\n # no handshake is shown: the manager answers each one itself.\n def tls_config(alpn : Array(String) = [\"http/1.1\"]) : TlsServerConfig\n # The longest a handshake waits for an order - less when its deadline\n # is nearer - and an order for the CA's polls.\n def wait_ms : Int32\n # The longest a handshake waits for an order - less when its deadline\n # is nearer - and an order for the CA's polls.\n def wait_ms=(value : Int32) : Int32\nend\n\nimpl Error for AutocertError\n def message : String\nend\n\nimpl AutocertCache for AutocertDirCache\n def get(key : String) : String | AutocertError | Nil\n def put(key : String, data : String) : AutocertError | ::Nil\n def delete(key : String) : AutocertError | ::Nil\nend\n" }, { "name": "std/base32", "path": "src/std/base32.iyi", - "sourceSha256": "f9b1992e89458803b9a096d9855dcb689258b6439f0f439499bc36f2e320dc4b", + "sourceSha256": "c4b5625277e958d088be569b2a557a95dca14407c260deadd43a2cbcb3536a42", "surface": "module std/base32\n\n# RFC 4648 section 6, the alphabet authenticator secrets are written in.\npub struct Base32\n def self.decode(text : String) : Array(UInt8) | Base32Error\n def self.encode(data : String | Array(UInt8), padding : Bool = true) : String\n def initialize\nend\n\n# What `decode` answers for a text that is not base32.\npub struct Base32Error\n def initialize(message : String)\n def to_s : String\nend\n\n# RFC 4648 section 7, the extended-hex alphabet: what DNSSEC's NSEC3\n# records (RFC 5155) write their hashed names in.\npub struct Base32Hex\n def self.decode(text : String) : Array(UInt8) | Base32Error\n def self.encode(data : String | Array(UInt8), padding : Bool = true) : String\n def initialize\nend\n\nimpl Error for Base32Error\n def message : String\nend\n" }, { "name": "std/base64", "path": "src/std/base64.iyi", - "sourceSha256": "ac315bc8855107def67fc606bffbc46414ce9327e30e22a96e462a4b1898d4fd", + "sourceSha256": "c7e6580b4fb0444710ff5f49390ec88d27dc408ff7eb7bef11c3df0910b1de2d", "surface": "module std/base64\n\npub struct Base64\n def self.decode(text : String) : String\n protected def self.decode_values : Array(Int32)\n def self.encode(data : String) : String\n def initialize\n protected def self.pairs_of(alphabet : String) : Array(Int32)\n def self.urlsafe_encode(data : String) : String\nend\n" }, { @@ -64,13 +64,13 @@ { "name": "std/big", "path": "src/std/big.iyi", - "sourceSha256": "979cc358dd97fd15026b0251c74420c2c3a6ddc1c6210d2d099c0105af7bbb6f", + "sourceSha256": "f04f68120f7ca38d5b5bfc2a33cc3b9ca4fe1034181d7d4a13d1dcf2004e65a9", "surface": "module std/big\n\npub struct BigDecimal\n def !=(other : BigDecimal) : Bool\n def %(other : BigDecimal) : BigDecimal\n def *(other : BigDecimal) : BigDecimal\n def *(other : Int32 | Int64) : BigDecimal\n def +(other : BigDecimal) : BigDecimal\n def +(other : Int32 | Int64) : BigDecimal\n def + : BigDecimal\n def -(other : BigDecimal) : BigDecimal\n def -(other : Int32 | Int64) : BigDecimal\n def - : BigDecimal\n def /(other : BigDecimal) : BigDecimal\n def /(other : Int32 | Int64) : BigDecimal\n def //(other : BigDecimal) : BigDecimal\n def abs : BigDecimal\n protected def self.align(a : BigDecimal, b : BigDecimal) : Tuple(BigInt, BigInt, Int32)\n # `self / other` rounded to `precision` fractional digits, half away from\n # zero. A quotient that is exact within `precision` digits is answered with\n # the fewest digits that write it: `1/4` is `0.25`, `6/3` is `2`. One that\n # is not keeps all `precision` digits, so the reader sees where it was cut.\n def div(other : BigDecimal, precision : Int32) : BigDecimal\n # The same value at another scale is the same key: `1.10`, `1.1` and\n # `11e-1` hash alike because `==` says they are equal. The digits without\n # their trailing zeros and the exponent that goes with them name the value;\n # multiplying a negative scale out made `1e1000000.hash` take 2.6 s.\n def hash : Int32\n def initialize(value : BigInt, scale : Int32 = 0)\n def initialize(num : Int32 = 0, scale : Int32 = 0)\n def initialize(num : Int64, scale : Int32 = 0)\n # Parses `[+-]digits[.digits][e[+-]digits]`; anything else is refused.\n def initialize(str : String)\n def inspect : String\n # Bounds on the place of the leading digit, from the limb count alone:\n # 10^(lo - 1) <= |self| < 10^hi. 30102 and 30103 in 100,000 bracket\n # log10(2) for each 32-bit limb.\n protected def magnitude_bounds : Tuple(Int64, Int64)\n def negate : BigDecimal\n def negative? : Bool\n # The same value with the smallest non-negative scale: trailing zeros of\n # the fraction are dropped and a negative scale is multiplied out.\n def normalized : BigDecimal\n def positive? : Bool\n def scale : Int32\n # The value without its trailing zeros, at most `limit` of them, and how\n # many went. Counted once in the digits and divided out once: one divmod\n # by ten per zero was quadratic, and the hash of `1.` and 40,000 zeros, or\n # `3.div(3, 40000)`, took 725 ms. A value that does not end in zero is\n # answered by one division by ten, without printing it.\n protected def strip_zeros(limit : Int32) : Tuple(BigInt, Int32)\n def to_big_d : BigDecimal\n def to_big_i : BigInt\n def to_f : Float64\n def to_f64 : Float64\n def to_i32 : Int32\n def to_i64 : Int64\n # Zero is `0` whatever its scale; every other value prints every digit of\n # its scale, so `1.10` stays `1.10`.\n def to_s : String\n def value : BigInt\n def zero? : Bool\nend\n\n# A binary floating-point number of any precision: `±mantissa · 2^exponent`,\n# the mantissa exactly *precision* bits long (128 unless asked), every\n# result rounded once, to the nearest value of that many bits, ties to even\n# - the rounding IEEE 754 gives a double, at any width. The other library's\n# BigFloat is GMP's `mpf`, which truncates and keeps more bits than it was\n# asked for; this one answers what MPFR does.\n#\n# a = BigFloat.new(2, 256)\n# a.sqrt.to_s\n# # => \"1.4142135623730950488016887242096980785696718753769480731766797379907324784621\"\n# BigFloat.new(\"0.1\", 24).to_f64 # => 0.10000000149011612\n#\n# An operation between two answers at the larger precision; with an\n# integer, at the BigFloat's. `to_s` writes the fewest decimal digits that\n# read back to the same value at its precision, positional from 1e-4 to\n# below 1e15 and with an exponent outside, as `Float64#to_s` writes. There\n# is no NaN or infinity: dividing by zero, the square root of a negative\n# number, a NaN or an infinite double, a precision under 1 and a string\n# that is no number are refused.\npub struct BigFloat\n def *(other : BigFloat) : BigFloat\n def *(other : Int32 | Int64 | BigInt) : BigFloat\n # The power, its exact value rounded once; a negative exponent is the\n # reciprocal of the power, rounded once.\n def **(exponent : Int32) : BigFloat\n def +(other : BigFloat) : BigFloat\n def +(other : Int32 | Int64 | BigInt) : BigFloat\n def + : BigFloat\n def -(other : BigFloat) : BigFloat\n def -(other : Int32 | Int64 | BigInt) : BigFloat\n def - : BigFloat\n def /(other : BigFloat) : BigFloat\n def /(other : Int32 | Int64 | BigInt) : BigFloat\n def ==(other : BigFloat) : Bool\n def abs : BigFloat\n def ceil : BigFloat\n protected def self.check(precision : Int32) : Nil\n # An integer at exactly the bits it has.\n protected def self.exactly(value : Int32 | Int64 | BigInt) : BigFloat\n protected def exponent : Int64\n def floor : BigFloat\n # Equal values hash alike whatever their precision: the mantissa without\n # its trailing zero bits.\n def hash : Int32\n def initialize(negative : Bool, mantissa : BigInt, exponent : Int64, precision : Int32)\n def inspect : String\n # The largest integer whose square is at most *value*, by Newton's\n # method from above.\n protected def self.isqrt(value : BigInt) : BigInt\n protected def mantissa : BigInt\n def negative? : Bool\n def self.new(value : Int32, precision : Int32 = DEFAULT_PRECISION) : BigFloat\n def self.new(value : Int64, precision : Int32 = DEFAULT_PRECISION) : BigFloat\n def self.new(value : BigInt, precision : Int32 = DEFAULT_PRECISION) : BigFloat\n # The double's exact value, rounded to *precision* bits.\n def self.new(value : Float64, precision : Int32 = DEFAULT_PRECISION) : BigFloat\n def self.new(value : BigRational, precision : Int32 = DEFAULT_PRECISION) : BigFloat\n def self.new(value : BigDecimal, precision : Int32 = DEFAULT_PRECISION) : BigFloat\n # A decimal - `-12.5`, `1e-30`, `6.02214076E23` - rounded to *precision*\n # bits; anything else is refused.\n def self.new(text : String, precision : Int32 = DEFAULT_PRECISION) : BigFloat\n # As `new`, nil for a string that is no number.\n def self.parse?(text : String, precision : Int32 = DEFAULT_PRECISION) : BigFloat | ::Nil\n # *digits* with its first digit at 10^*point*, as `Float64#to_s` puts\n # a point: positional from 10^-4 to 10^14, an exponent outside.\n protected def self.place(digits : String, point : Int64) : String\n def positive? : Bool\n def precision : Int32\n # Under one half is zero; from there the fraction is at most\n # *precision* bits, compared with one half exactly.\n def round : BigFloat\n # *num*/*den* · 2^*shift* (both non-negative, *den* not zero), rounded to\n # *precision* bits, ties to even: every value here is made by this, from\n # an exact one.\n protected def self.rounded(negative : Bool, num : BigInt, den : BigInt, shift : Int64, precision : Int32) : BigFloat\n # The square root, rounded once. The mantissa is widened to at least\n # 2·precision + 4 bits over an even power of two, so the integer root\n # has two bits past the ones kept, and a root that is not exact carries a\n # half below its last bit into the rounding, which no tie can be.\n def sqrt : BigFloat\n # The exact sum, rounded once. An addend more than *precision* + 2 bits\n # below the other cannot move it - it is under a quarter of the larger's\n # last place, even where that place halves below a power of two - and\n # is not shifted out to a size it would only be rounded away from.\n protected def sum(other : BigFloat, precision : Int32) : BigFloat\n # Toward zero, floor, ceiling, and to the nearest integer, ties to even.\n def to_big_i : BigInt\n # The exact value as a fraction.\n def to_big_r : BigRational\n # The double nearest, ties to even; past the largest double, infinite,\n # and below the smallest, zero.\n def to_f64 : Float64\n # The fewest decimal digits that read back as this value at its\n # precision, the nearest such when there are two; written as\n # `Float64#to_s` writes a double.\n def to_s : String\n # The place above the leading bit: 2^(top - 1) <= |self| < 2^top.\n protected def top : Int64\n def trunc : BigFloat\n protected def trunc_whole : BigInt\n # This value at another precision, rounded if it is fewer bits.\n def with_precision(bits : Int32) : BigFloat\n def zero? : Bool\nend\n\npub struct BigInt\n # Three-way comparison is `<=>`, from `impl Comparable` below.\n def !=(other : BigInt) : Bool\n def !=(other : Int64) : Bool\n def !=(other : Int32) : Bool\n def %(other : BigInt) : BigInt\n def %(other : Int64) : BigInt\n def %(other : Int32) : BigInt\n # Bitwise AND (&) with two's complement semantics\n def &(other : BigInt) : BigInt\n # Multiplication\n def *(other : BigInt) : BigInt\n def *(other : Int64) : BigInt\n def *(other : Int32) : BigInt\n # Exponentiation by squaring: a ** exp\n def **(exp : Int32) : BigInt\n def **(exp : Int64) : BigInt\n def **(exp : BigInt) : BigInt\n # Addition\n def +(other : BigInt) : BigInt\n def +(other : Int64) : BigInt\n def +(other : Int32) : BigInt\n def + : BigInt\n # Subtraction: the sum with the other's sign turned, without copying it.\n def -(other : BigInt) : BigInt\n def -(other : Int64) : BigInt\n def -(other : Int32) : BigInt\n def - : BigInt\n def //(other : BigInt) : BigInt\n def //(other : Int64) : BigInt\n def //(other : Int32) : BigInt\n def <(other : Int64) : Bool\n def <(other : Int32) : Bool\n # Bitwise Left Shift: a << count\n def <<(count : Int32) : BigInt\n def <=(other : Int64) : Bool\n def <=(other : Int32) : Bool\n # Comparison overloads for Int64 and Int32\n def <=>(other : Int64) : Int32\n def <=>(other : Int32) : Int32\n def ==(other : Int64) : Bool\n def ==(other : Int32) : Bool\n def >(other : Int64) : Bool\n def >(other : Int32) : Bool\n def >=(other : Int64) : Bool\n def >=(other : Int32) : Bool\n # Bitwise Right Shift (arithmetic): a >> count\n def >>(count : Int32) : BigInt\n # Bitwise XOR (^) with two's complement semantics\n def ^(other : BigInt) : BigInt\n # Returns the absolute value of self.\n def abs : BigInt\n def abs2 : BigInt\n # The digits of this value's magnitude, least significant first, onto\n # `sink`: exactly `width` of them when `width` is positive, zeros above.\n #\n # A long value is split by `powers[level]`, a power of the chunk about\n # half its size, and each half is written by the level below - the low\n # half to exactly the digits that power stands for, so its leading zeros\n # are kept. Dividing by one chunk at a time is a hardware division per\n # limb per nine digits, n^2 / 9 of them; the split does that only for the\n # short pieces at the bottom and multiplies its way through the rest,\n # dividing by `divmod_recursive`.\n protected def append_digits(radix : UInt64, chunk : UInt64, per : Int32, powers : Array(BigInt), level : Int32, width : Int32, sink : Array(UInt8)) : Nil\n # The bits |value| takes to write.\n protected def self.bit_length(value : BigInt) : Int32\n # `count` bits of this value, not negative, from bit `from` up, in one\n # copy.\n protected def bits(from : Int32, count : Int32) : BigInt\n # The bits one digit of a power-of-two base takes; for any other base,\n # the fewest bits a digit can carry, so that a limb's worth of bits\n # bounds how many digits it makes.\n protected def self.bits_per_digit(base : Int32) : Int32\n # Magnitude comparison: compares |self| and |other|.\n # Returns -1 if |self| < |other|, 0 if |self| == |other|, 1 if |self| > |other|.\n def cmp_abs(other : BigInt) : Int32\n protected def self.digit_byte(value : UInt64) : UInt8\n # A digit byte's value in any base up to 36, or 36 for a byte that is a\n # digit in none.\n protected def self.digit_value(byte : UInt8) : Int32\n # This value's `2n` bits and `low`'s `n` by `b`, `top` and `bottom` its\n # halves of `n` bits each.\n protected def div_three_by_two(low : BigInt, b : BigInt, top : BigInt, bottom : BigInt, n : Int32) : Tuple(BigInt, BigInt)\n # This value, under `divisor * 2^n`, by `divisor`, exactly `n` bits long\n # and `n` even until it is short enough for long division: the top\n # `n + n/2` bits by the divisor, then the remainder and the next `n/2`.\n # Each is a division by the divisor's top half, a product by its bottom\n # half, and at most two corrections, the divisor's top bit being set.\n protected def div_two_by_one(divisor : BigInt, n : Int32) : Tuple(BigInt, BigInt)\n # Division and Modulo. Returns {quotient, remainder}.\n # Division truncates towards zero; remainder follows the sign of the dividend (self).\n #\n # A long divisor and a long quotient go to `divmod_recursive`, the\n # recursive division `to_s` splits its digits with, on the magnitudes,\n # and take their signs after. Long division costs the product of the\n # quotient's length and the divisor's, and every division went to it: a\n # value twice a divisor of 524,288 bits took 282 ms in a release build\n # and one of 2,097,152 bits 4,929 ms, four times over per doubling,\n # where these take 179 and 1,964.\n def divmod(other : BigInt) : Tuple(BigInt, BigInt)\n # Knuth's Algorithm D, with the signs `divmod` gives.\n protected def divmod_long(other : BigInt) : Tuple(BigInt, BigInt)\n # |self| divided by |other|, two limbs or more, for a quotient under\n # 2^32: *shift* is the divisor's bit length less 32, and |self| over\n # 2^shift, A, fits 63 bits. With B the divisor's top 32 bits, the\n # quotient is estimated as A / (B + 1), which is never more than the\n # quotient and short of it by less than A / B^2 + 1, at most three; a - q\n # b is taken in one pass and the divisor taken off what is left while it\n # is not below it.\n protected def divmod_one_limb(other : BigInt, shift : Int32, q_sign : Int32, r_sign : Int32) : Tuple(BigInt, BigInt)\n # The quotient and remainder of this value, not negative, by `divisor`,\n # positive, for the time a few products of half the length take: long\n # division costs the product of the two lengths, and the split print\n # divides by a power half as long as the value, so `to_s` was quadratic -\n # 787 ms for 381,698 digits and 3,107 ms for twice as many. Burnikel and\n # Ziegler's recursive division, in the form Python's `_pylong` writes it,\n # by bits. A value past twice the divisor's length has its top divided\n # first, as long division takes a digit at a time; the rest is shifted so\n # that the divisor's length halves evenly down to where long division is\n # the faster, and the remainder shifted back.\n protected def divmod_recursive(divisor : BigInt) : Tuple(BigInt, BigInt)\n # Returns true if this number is even.\n def even? : Bool\n # The big-endian *bytes* as a non-negative BigInt: a key, a signature, a\n # coordinate as cryptography writes them. Four bytes a limb, the low\n # limb from the last four.\n def self.from_bytes(bytes : Array(UInt8)) : BigInt\n # The value of `digits`, most significant first, in `base`: the inverse\n # of `to_s(base)`, and built the same way. A power-of-two base packs the\n # digits' bits into limbs directly. Any other splits the digits at a\n # power of the chunk (`append_digits`), so that the halves meet in one\n # Karatsuba product each, and runs the short pieces at the bottom through\n # one multiply-add per chunk in place. This had shifted or multiplied a\n # whole new BigInt per digit - per nine digits in decimal - and 80,000\n # hex digits took more than a second.\n protected def self.from_digits(digits : Array(UInt8), base : Int32) : BigInt\n # Parses a hexadecimal string (with or without '0x' prefix).\n def self.from_hex(str : String) : BigInt\n # Greatest Common Divisor (Euclidean algorithm)\n def gcd(other : BigInt) : BigInt\n def gcd(other : Int64) : BigInt\n def gcd(other : Int32) : BigInt\n # FNV-1a over the sign and every limb, folded to 31 bits. Wrapping\n # multiplication (`&*`) is the point: a checked product would overflow on\n # the second limb, and a hash has to answer for any size.\n def hash : Int32\n # Raw constructor from sign and limbs. Trims leading zero limbs to enforce\n # the canonical representation invariant: zero has empty limbs and sign 0.\n def initialize(sign : Int32, limbs : Array(UInt64))\n # The inverse of self modulo an odd *mod*, in 0...mod, or nil when the\n # two share a factor (Python's `pow(self, -1, mod)`, which raises\n # there): for a value that is a secret, such as RSA's blinding factor.\n # The steps taken, their branches and the words they read depend on the\n # modulus' length alone (`Montgomery#inverse`); an even modulus is a\n # misuse, refused. As with `pow_mod_secret`, self is reduced by `%`\n # first, whose time depends on it: a caller hands in a value below the\n # modulus.\n def inverse_mod_secret(mod : BigInt) : BigInt | ::Nil\n # Least Common Multiple\n def lcm(other : BigInt) : BigInt\n def lcm(other : Int64) : BigInt\n def lcm(other : Int32) : BigInt\n def limbs : Array(UInt64)\n # Whether the *n* limbs at *r* are at least the *m* at *v*.\n protected def self.limbs_at_least(r : Pointer(UInt64), n : Int32, v : Pointer(UInt64), m : Int32) : Bool\n # Returns the negated value of self (-self).\n def negate : BigInt\n # Returns true if this number is strictly negative (< 0).\n def negative? : Bool\n # Constructs a BigInt from an Int64.\n def self.new(val : Int64) : BigInt\n # Constructs a BigInt from an Int32.\n def self.new(val : Int32) : BigInt\n # Parses a BigInt from a string in the given base (2..36, default 10).\n # Supports leading whitespace, optional sign ('+' or '-'), and base prefixes\n # (0x/0X for hex, 0b/0B for binary, 0o/0O for octal).\n def self.new(str : String, base : Int32 = 10) : BigInt\n # Returns true if this number is odd.\n def odd? : Bool\n # Parses a string with full error checking.\n def self.parse(str : String, base : Int32 = 10) : BigInt\n # Returns true if this number is strictly positive (> 0).\n def positive? : Bool\n # Modular exponentiation: (self ** exp) % mod, in 0...mod whatever the\n # base's sign. An odd modulus, which is every modulus RSA, Diffie-Hellman\n # and DSA work in, goes to `Montgomery` with a sliding window; an even one\n # keeps square-and-multiply with a division per step.\n #\n # Each step here was a product and a long division of twice the\n # modulus' length, and a 2048-bit modulus with a 2048-bit exponent took\n # 19.0 ms in a --release build where Python 3.14's `pow` took 13.3 (3072\n # bits: 63.2 and 42.3, 4096: 168.7 and 100.3). Montgomery's takes 2.5 ms,\n # 8.5 and 20.6 in the run of bench/std_big_exercise.sh that measured\n # Python at 13.7, 45.4 and 102.1, which prints these each run.\n def pow_mod(exp : BigInt, mod : BigInt) : BigInt\n def pow_mod(exp : Int64, mod : Int64) : BigInt\n def pow_mod(exp : Int32, mod : Int32) : BigInt\n # `pow_mod` for an exponent that is a secret, a private key's: a fixed\n # window, every entry of its table read for each window and the one\n # wanted kept by a mask, so neither a branch nor a memory address depends\n # on the exponent's bits, and as many windows as the longer of the\n # exponent and the modulus takes, so the exponent's length does not show\n # either. The modulus has to be odd (a Montgomery modulus): an even one\n # is a misuse, refused.\n #\n # What it does not hide: the base is reduced by `%`, whose time depends\n # on the base and the modulus, so a caller with a secret base (RSA's\n # CRT halves of a message) blinds it first, as std/rsa's signing does.\n def pow_mod_secret(exp : BigInt, mod : BigInt) : BigInt\n def sign : Int32\n # Number of limbs currently allocated.\n def size : Int32\n def to_big : BigInt\n def to_big_d : BigDecimal\n def to_big_i : BigInt\n def to_big_r : BigRational\n # The value big-endian in exactly *size* bytes, zeros in front: the\n # inverse of `from_bytes` at a fixed width, which is how a signature or a\n # coordinate is written. Nil for a negative value or one that does not\n # fit, never a value cut short.\n def to_bytes(size : Int32) : Array(UInt8) | ::Nil\n def to_i : Int32\n # Converts to Int32, raising an overflow error if the value exceeds Int32 range.\n def to_i32 : Int32\n # Converts to Int32 if the value fits, or nil if it overflows.\n def to_i32? : Int32 | ::Nil\n # Converts to Int64, raising an overflow error if the value exceeds Int64 range.\n def to_i64 : Int64\n # Converts to Int64 if the value fits, or nil if it overflows.\n def to_i64? : Int64 | ::Nil\n # Converts this BigInt to a string in the given base (default 10).\n # Bases 2..36 supported. Digits above 9 are lowercase 'a'..'z'.\n #\n # The digits are gathered least significant first and written into the\n # string once. A base that is a power of two reads them straight off the\n # limbs' bits; any other divides a copy of the limbs in place by the\n # largest power of the base a limb holds - 10^9 for decimal - and takes\n # that many digits from each remainder. This had built the text with\n # `res = res + s`, a copy of everything so far per chunk, and divided by\n # a fresh one-limb BigInt per chunk, or per digit outside bases 2, 10\n # and 16: 3 ** 400000 took a second in decimal and in binary, and nine\n # and a half in base 7.\n def to_s(base : Int32) : String\n def to_s : String\n # Helper: bitwise magnitude operation with sign inversion\n protected def twos_mag : BigInt\n # The value of `count` digits from `from`: the high part times the power\n # of the chunk that the low part's digits stand for, plus the low part.\n protected def self.value_of_digits(digits : Array(UInt8), from : Int32, count : Int32, base : UInt64, per : Int32, powers : Array(BigInt)) : BigInt\n # |value| over 2^shift, for one under 2^64: the limbs from bit *shift*.\n protected def self.window(limbs : Array(UInt64), shift : Int32) : UInt64\n # Returns true if this number is zero.\n def zero? : Bool\n # Bitwise OR (|) with two's complement semantics\n def |(other : BigInt) : BigInt\n # Bitwise NOT (~a = -a - 1 in two's complement)\n def ~ : BigInt\nend\n\npub struct BigRational\n def !=(other : BigRational) : Bool\n def *(other : BigRational) : BigRational\n def *(other : Int32 | Int64 | BigInt) : BigRational\n def **(exp : Int32) : BigRational\n def +(other : BigRational) : BigRational\n def +(other : Int32 | Int64 | BigInt) : BigRational\n def + : BigRational\n def -(other : BigRational) : BigRational\n def -(other : Int32 | Int64 | BigInt) : BigRational\n def - : BigRational\n def /(other : BigRational) : BigRational\n def /(other : Int32 | Int64 | BigInt) : BigRational\n def abs : BigRational\n def denominator : BigInt\n # A rational is stored reduced, so equal values have equal parts.\n def hash : Int32\n def initialize(numerator : BigInt, denominator : BigInt)\n def initialize(num : Int64 = 0_i64, den : Int64 = 1_i64)\n def initialize(num : Int32, den : Int32 = 1)\n def initialize(str : String)\n def inspect : String\n def inv : BigRational\n def negate : BigRational\n def negative? : Bool\n def numerator : BigInt\n def positive? : Bool\n def to_big_d : BigDecimal\n def to_big_i : BigInt\n def to_big_r : BigRational\n # The Float64 nearest this value, ties to even, as python's\n # `float(Fraction)` answers. This had gone through `to_big_d`, twenty\n # fractional digits: every value under 5e-21 was 0.0 and every one under\n # 1e-3 lost significant digits. The quotient is taken to 55 or 56 bits -\n # 55 at the smallest exponent, where the subnormals start - with a sticky\n # low bit for a nonzero remainder, rounded to the bits a Float64 keeps (53,\n # fewer as a subnormal), and scaled by the power of two it was shifted by,\n # which is exact: a value past the largest Float64 is Infinity.\n def to_f64 : Float64\n def to_s : String\n def zero? : Bool\nend\n\nimpl Std::Traits::Comparable for BigInt\n def <=>(other : self) : Int32\nend\n\nimpl Std::Traits::Num for BigInt\n def self.zero : self\n def self.one : self\n def add(other : self) : self\n def mul(other : self) : self\nend\n\nimpl Std::Traits::Hashable for BigInt\n def hash_key : Int32\nend\n\nimpl Std::Traits::Comparable for BigDecimal\n def <=>(other : self) : Int32\nend\n\nimpl Std::Traits::Num for BigDecimal\n def self.zero : self\n def self.one : self\n def add(other : self) : self\n def mul(other : self) : self\nend\n\nimpl Std::Traits::Hashable for BigDecimal\n def hash_key : Int32\nend\n\nimpl Std::Traits::Comparable for BigRational\n def <=>(other : self) : Int32\nend\n\nimpl Std::Traits::Num for BigRational\n def self.zero : self\n def self.one : self\n def add(other : self) : self\n def mul(other : self) : self\nend\n\nimpl Std::Traits::Hashable for BigRational\n def hash_key : Int32\nend\n\nimpl Std::Traits::Comparable for BigFloat\n def <=>(other : self) : Int32\nend\n\nimpl Std::Traits::Hashable for BigFloat\n def hash_key : Int32\nend\n\nstruct ::Int32\n def *(other : Std::Big::BigInt) : Std::Big::BigInt\n def +(other : Std::Big::BigInt) : Std::Big::BigInt\n def -(other : Std::Big::BigInt) : Std::Big::BigInt\n def <=>(other : Std::Big::BigInt) : Int32\n def ==(other : Std::Big::BigInt) : Bool\n def to_big : Std::Big::BigInt\n def to_big_d : Std::Big::BigDecimal\n def to_big_i : Std::Big::BigInt\n def to_big_r : Std::Big::BigRational\nend\n\nstruct ::Int64\n def *(other : Std::Big::BigInt) : Std::Big::BigInt\n def +(other : Std::Big::BigInt) : Std::Big::BigInt\n def -(other : Std::Big::BigInt) : Std::Big::BigInt\n def <=>(other : Std::Big::BigInt) : Int32\n def ==(other : Std::Big::BigInt) : Bool\n def to_big : Std::Big::BigInt\n def to_big_d : Std::Big::BigDecimal\n def to_big_i : Std::Big::BigInt\n def to_big_r : Std::Big::BigRational\nend\n\nclass ::String\n def to_big_d : Std::Big::BigDecimal\n def to_big_i : Std::Big::BigInt\n def to_big_r : Std::Big::BigRational\nend\n" }, { "name": "std/bit_array", "path": "src/std/bit_array.iyi", - "sourceSha256": "8010f7fd4ea052dda095999ec86125161f4755c63056f3940adf0303cf6041cc", + "sourceSha256": "486ae94e306d0dd415163a66f4d9e063beaee36aaffc2533ddacef1f807a676e", "surface": "module std/bit_array\n\npub alias BitArray = ::BitArray\n\nstruct ::BitArray\n def ==(other : BitArray) : Bool\n def ==(other : Object) : Bool\n def [](index : Int) : Bool\n def []=(index : Int, value : Bool) : Bool\n def []?(index : Int) : Bool | ::Nil\n def all? : Bool\n def any? : Bool\n def clone : self\n def count(item : Bool) : Int32\n def count : Int32\n def dup : self\n def each(& : (Bool -> Nil)) : Nil\n def each_with_index(offset : Int = 0, & : (Bool, Int32 -> Nil)) : Nil\n def empty? : Bool\n def fill(value : Bool, start : Int, count : Int) : self\n def fill(value : Bool) : self\n def first : Bool\n def first? : Bool | ::Nil\n def hash : Int32\n def includes?(obj : Bool) : Bool\n def index(obj : Bool, offset : Int = 0) : Int32 | ::Nil\n def initialize(size : Int, initial : Bool = false)\n def inspect(io : IyiIO) : Nil\n def inspect : String\n def invert : Nil\n def last : Bool\n def last? : Bool | ::Nil\n def self.new(size : Int, & : (Int32 -> _)) : self\n def none? : Bool\n def one? : Bool\n def reverse_in_place : self\n def rindex(obj : Bool, offset : Int = @size - 1) : Int32 | ::Nil\n def rotate_in_place(n : Int = 1) : self\n def size : Int32\n def to_s(io : IyiIO) : Nil\n # Into one builder: a string grown by a character per bit copied it per\n # bit, and 200,000 bits took 46 s to print.\n def to_s : String\n def to_slice : Std::Slice::Bytes\n def toggle(index : Int) : Nil\n def unsafe_fetch(index : Int) : Bool\n def unsafe_put(index : Int, value : Bool) : Bool\nend\n" }, { @@ -82,19 +82,19 @@ { "name": "std/box", "path": "src/std/box.iyi", - "sourceSha256": "f34ecb7faa9c79a4035274e255ae6ce0231a97a27be6f2954acb1ab23e47c9a8", + "sourceSha256": "01edf14c2f152a14a83181424c041788c314b50b117392856f849e6e9d4b950e", "surface": "module std/box\n\npub class Box(T)\n # Turns object into a Void*.\n #\n # Copies object to the heap inside a Box container if needed.\n def self.box(object : T) : ::Pointer(Void)\n def initialize(object : T)\n def object : T\n # Unboxes a Void* into an object of type T.\n #\n # Specifying T is required: `Box(T).unbox(data)`. A null pointer is nil\n # when T admits nil, as the other library answers it for `String?` and\n # every other union of references and nil, and is refused otherwise. It\n # was refused for every T, so `Box(String?).unbox` of the null a C\n # callback is handed when there is no data panicked \"Unboxing null\n # pointer\" where nil is the answer the type has room for.\n def self.unbox(pointer : ::Pointer(Void)) : T\nend\n" }, { "name": "std/bzip2", "path": "src/std/bzip2.iyi", - "sourceSha256": "e6ddde4436c552246e884befc9e59a8614c3c60ba2878550af35f2d6a53baa97", + "sourceSha256": "d1b80e3d12da6c0581ea495159c8b422a6dab2c5d2c61115f293412599e84a18", "surface": "module std/bzip2\n\npub struct Bzip2\n # *data* as one bzip2 stream with blocks of *level* * 100,000 bytes,\n # 1 to 9 (bzip2's -1 to -9); any other level is a panic.\n def self.compress(data : String, level : Int32 = 9) : String\n # Every byte *data*'s streams hold, or the error the first wrong thing in\n # them is. Bytes after the last stream that do not start another are\n # ignored, as bzip2(1) ignores them.\n def self.decompress(data : String) : String | Bzip2Error\n def initialize\nend\n\n# What `Bzip2Reader#read` and `Bzip2.decompress` answer for a stream they\n# cannot read: one that is corrupt, whose CRC does not match, or that ends\n# early.\npub struct Bzip2Error\n def initialize(message : String)\n def to_s : String\nend\n\n# A bzip2 stream read a piece at a time: the compressed bytes come from\n# the block, the decompressed ones go out through `read`, and memory\n# stays at one block's links and a 64 KiB buffer.\n#\n# ```\n# body = Bzip2Reader.new { file.read_bytes(65536) }\n# while (piece = body.read(65536)!) != \"\"\n# ...\n# end\n# ```\npub class Bzip2Reader\n # Whether every stream has ended, its CRCs checked, and every byte been\n # read: the next `read` answers \"\".\n def finished? : Bool\n # *source* answers the next bytes of the compressed stream, nil (or \"\")\n # when there are no more. It is called only when the decoder has no\n # bytes in hand for what it must read next, so it is never asked for\n # anything past a stream's end - except that the input ends only where\n # no stream follows, which takes a look at the next four bytes.\n def initialize(&source : (-> String | ::Nil))\n # Bytes the source gave beyond the last stream, for a caller that frames\n # other data after it; \"\" before the input has ended.\n def leftover : String\n # Up to *max* decompressed bytes, 64 KiB at most; \"\" once every stream\n # has ended and its CRCs been checked. A read that has bytes when a\n # block ends answers them rather than reading the next block, so it can\n # answer fewer than *max* before the end.\n #\n # A corrupt stream, a CRC that does not match, or input that ends early\n # is a `Bzip2Error`, after which every read answers the same error. A\n # block's bytes are given out before its CRC is known, as the CRC is\n # over them; the read that gives its last byte checks it, and answers\n # the error instead of those bytes. A *max* below 1 is a panic.\n def read(max : Int32 = 65536) : String | Bzip2Error\nend\n\nimpl Error for Bzip2Error\n def message : String\nend\n" }, { "name": "std/capsule", "path": "src/std/capsule.iyi", - "sourceSha256": "58b69bec13b3e4c432f8bcb087b7b304614bab4ee0e7c6eb64663174e1077330", + "sourceSha256": "10b7d3dbc95376b49ec94e091b12c0775a83a7057acbe4f4f9d7692bbf9bd76c", "surface": "module std/capsule\n\npub struct Capsule\n def capsule_type : UInt64\n def close_webtransport_session? : Bool\n def datagram? : Bool\n # Decodes a single capsule from bytes starting at offset, raising on error.\n def self.decode(bytes : Bytes, offset : Int32 = 0, strict : Bool = false) : ::Tuple(Capsule, Int32)\n # Decodes a single capsule from bytes starting at offset.\n # Returns {Capsule, total_bytes_consumed}.\n # Refuses truncated capsules or lengths that overrun the buffer.\n # Decodes a single capsule, returning nil if truncated or length overruns buffer.\n def self.decode?(bytes : Bytes, offset : Int32 = 0, strict : Bool = false) : ::Tuple(Capsule, Int32) | ::Nil\n def drain_webtransport_session? : Bool\n # Encodes the capsule to stream wire format:\n # Capsule Type (i)\n # Capsule Length (i)\n # Capsule Value (..)\n def encode : Bytes\n def initialize(capsule_type : UInt64, value : Bytes)\n # Returns true if this capsule type is unrecognized by base specifications.\n def unknown? : Bool\n def value : Bytes\nend\n\npub struct CapsuleProtocol\n HEADER_NAME = \"Capsule-Protocol\"\n HEADER_VALUE = \"?1\"\n def self.header_name : String\n def self.header_value : String\n def initialize\n # True when the value, parsed as an Item Structured Field (RFC 8941\n # Section 4.2), is the Boolean true. Its parameters are parsed and then\n # ignored, as RFC 9297 Section 3.4 requires of unknown ones; a value that\n # does not parse as an Item is as if the field were absent.\n def self.is_capsule_protocol_header?(value : String) : Bool\n # Checks if a header key-value pair enables the Capsule Protocol.\n def self.valid_header?(name : String, value : String) : Bool\nend\n\n# Capsule stream processing enforcing RFC 9297 Section 3.2 rules:\n# - Unknown capsule types are skipped rather than erroring (extensibility).\n# - Truncated capsules and length overruns are strictly refused.\npub struct CapsuleReader\n def initialize\n # Decodes all capsules in buffer into an array.\n # Preserves unknown capsules in the list for inspection.\n def self.read_all(bytes : Bytes, strict : Bool = false) : Array(Capsule)\n # Decodes capsules from stream buffer, actively skipping unknown types\n # and returning only recognized capsules, demonstrating RFC 9297 extensibility.\n def self.read_known(bytes : Bytes, strict : Bool = false) : Array(Capsule)\n # Decodes capsules and reports both known capsules and the count of skipped unknown types.\n def self.read_with_skip_count(bytes : Bytes, strict : Bool = false) : ::Tuple(Array(Capsule), Int32)\nend\n\n# Standard and extended capsule types.\npub struct CapsuleType\n DATAGRAM = 0_u64\n CLOSE_WEBTRANSPORT_SESSION = 10307_u64\n DRAIN_WEBTRANSPORT_SESSION = 30894_u64\n def initialize\nend\n\npub struct ExtendedConnect\n SETTINGS_ENABLE_CONNECT_PROTOCOL = 8_u64\n HEADER_METHOD = \":method\"\n HEADER_PROTOCOL = \":protocol\"\n HEADER_SCHEME = \":scheme\"\n HEADER_AUTHORITY = \":authority\"\n HEADER_PATH = \":path\"\n HEADER_STATUS = \":status\"\n METHOD_CONNECT = \"CONNECT\"\n PROTOCOL_WEBTRANSPORT = \"webtransport\"\n PROTOCOL_WEBSOCKET = \"websocket\"\n def initialize\n def self.settings_id : UInt64\n # Validates whether the given parameters form a valid Extended CONNECT request.\n def self.valid_request?(method : String, protocol : String | ::Nil, scheme : String | ::Nil, authority : String | ::Nil, path : String | ::Nil) : Bool\n # Validates whether a response status code establishes the Extended CONNECT tunnel (2xx).\n def self.valid_response?(status : Int32) : Bool\nend\n\npub struct HttpDatagram\n MAX_QUARTER_STREAM_ID = 1152921504606846975_u64\n # Decodes an HTTP Datagram from a byte slice, raising on truncation or on\n # a Quarter Stream ID past 2^60 - 1.\n # Overlong quarter-stream IDs in strict mode raise the varint overlong\n # sentence rather than pretending the buffer was truncated.\n def self.decode(bytes : Bytes, offset : Int32 = 0, strict : Bool = false) : ::Tuple(HttpDatagram, Int32)\n # Decodes an HTTP Datagram from a byte slice.\n # Decodes an HTTP Datagram, returning nil if truncated or if the Quarter\n # Stream ID is past 2^60 - 1, which RFC 9297 Section 2.1 makes an error.\n def self.decode?(bytes : Bytes, offset : Int32 = 0, strict : Bool = false) : ::Tuple(HttpDatagram, Int32) | ::Nil\n # Encodes this datagram into the wire format:\n # Quarter Stream ID (i)\n # HTTP Datagram Payload (..)\n def encode : Bytes\n # Creates an HTTP Datagram associated with the given HTTP/3 stream ID,\n # which is a client-initiated bidirectional stream: RFC 9297 Section 2.1\n # associates a datagram with no other kind, and that is why the division\n # by four always comes out even. Any other stream ID is refused. It was\n # divided down instead, so `from_stream_id(19_u64, payload)` was a\n # datagram for stream 16 - another request's - and `stream_id` answered\n # 16. A stream ID past 2^62 - 1 is left to `new`, whose sentence names\n # the quarter it would be.\n def self.from_stream_id(stream_id : UInt64, payload : Bytes) : self\n def initialize(quarter_stream_id : UInt64, payload : Bytes)\n def payload : Bytes\n def quarter_stream_id : UInt64\n # Maps the quarter stream ID back to the associated HTTP/3 stream ID.\n # Stream ID = Quarter Stream ID * 4.\n def stream_id : UInt64\nend\n\npub struct VarInt\n MAX = 4611686018427387903_u64\n MAX_1BYTE = 63_u64\n MAX_2BYTE = 16383_u64\n MAX_4BYTE = 1073741823_u64\n MAX_8BYTE = 4611686018427387903_u64\n # Decodes a QUIC variable-length integer from *bytes* starting at *offset*.\n # Raises on truncated buffers, reserved values, or overlong encodings (if strict).\n def self.decode(bytes : Bytes, offset : Int32 = 0, strict : Bool = false) : ::Tuple(UInt64, Int32)\n # Decodes a QUIC variable-length integer, returning nil if truncated, overlong, or reserved.\n def self.decode?(bytes : Bytes, offset : Int32 = 0, strict : Bool = false) : ::Tuple(UInt64, Int32) | ::Nil\n # Strict decoding shorthand: refuses overlong and reserved encodings.\n def self.decode_strict(bytes : Bytes, offset : Int32 = 0) : ::Tuple(UInt64, Int32)\n def self.decode_strict?(bytes : Bytes, offset : Int32 = 0) : ::Tuple(UInt64, Int32) | ::Nil\n # Encodes value using an explicit length (1, 2, 4, or 8 bytes).\n # Used to produce canonical encodings or explicit multi-byte encodings as\n # specified in RFC 9000 Appendix A (e.g. 37 encoded in 2, 4, or 8 bytes).\n def self.encode(value : UInt64, length : Int32) : Bytes\n # Encodes value into its canonical (minimum-length) QUIC varint representation.\n def self.encode(value : UInt64) : Bytes\n # Calculates the minimum number of bytes required to canonically encode value.\n def self.encoded_size(value : UInt64) : Int32\n def initialize\n # Checks if value can be represented as a 62-bit QUIC varint.\n def self.valid_value?(value : UInt64) : Bool\nend\n" }, { @@ -112,43 +112,43 @@ { "name": "std/compress", "path": "src/std/compress.iyi", - "sourceSha256": "e0bdcae3d05fb329e3c1561ac6b51291081f287121309e0c313ca2d342fc603b", + "sourceSha256": "5720c6b5cb786da0be2c4c29367d3037d9bc43fc432149cab26e5093bcfc96c0", "surface": "module std/compress\n\n# What `Inflater#read` answers for a stream it cannot read: one that is\n# corrupt, whose checksum or length does not match, or that ends early.\n# `message` is the sentence the whole-string calls panic with.\npub struct CompressError\n def initialize(message : String)\n def to_s : String\nend\n\npub struct Deflate\n # *data* as a DEFLATE stream, at *level*: 1 is fastest, 9 smallest, 6\n # (the default) zlib's own balance, 0 stored blocks only; anything else\n # is a panic. The same level writes what zlib's would size for size,\n # within the margins the exercise records.\n def self.compress(data : String, level : Int32 = 6) : String\n def self.decompress(data : String) : String\n # The bytes, and how many of the stream's bytes they took: an envelope\n # reads its trailer from there.\n #\n # *expected* is how long the caller thinks the result is, so that the\n # result is one allocation rather than a guess grown by doubling; -1,\n # the default, is no idea, and the guess is three times the stream's\n # bytes. Held to what the stream could expand to, so a length that lies\n # is not an allocation it asked for.\n def self.inflate(data : String, start : Int32, expected : Int64 = -1_i64) : ::Tuple(String, Int32)\n def initialize\nend\n\n# A compressed stream written a piece at a time. Each `compress` codes its\n# piece in blocks as `Deflate.compress` does and ends in zlib's sync\n# flush: an empty stored block, so everything written so far ends on a\n# byte boundary and decodes without waiting for more. The window is kept\n# from piece to piece - the next piece's matches reach back into the last\n# 32 KiB of the ones before it, as zlib's do across a sync flush - so a\n# stream written in pieces costs little more than the same bytes written\n# whole. `finish` writes the final block, empty, and the envelope's\n# trailer.\npub class Deflater\n # The compressed bytes for *data*: the envelope's header on first use,\n # then *data* as complete non-final blocks and the sync flush. \"\" for\n # empty data after the first call. After `finish`, a panic.\n def compress(data : String) : String\n # The final block and the envelope's trailer. Once only: a second call\n # is a panic.\n def finish : String\n # *level* is `Deflate.compress`'s: 0 stores, 1 is fastest, 9 smallest,\n # 6 the default. *window_bits* is the base-2 logarithm of how far back a\n # match may reach, 8 to 15: 15 is DEFLATE's whole 32 KiB, and a smaller\n # one is what a decoder that keeps less can read - RFC 7692's\n # `max_window_bits`. Anything else for either is a panic.\n def initialize(envelope : Envelope, level : Int32 = 6, window_bits : Int32 = 15)\nend\n\n# What a DEFLATE stream travels in: nothing (RFC 1951), zlib's two-byte\n# header and Adler-32 (RFC 1950), or gzip's header and CRC-32 and length\n# (RFC 1952).\npub enum Envelope : Int32\n Raw = 0\n Zlib = 1\n Gzip = 2\nend\n\npub struct Gzip\n # The ten-byte header with nothing optional set, an mtime of zero, the\n # level's XFL and an unknown OS; then the CRC-32 and the length modulo\n # 2^32, little-endian. *level* as `Deflate.compress`.\n def self.compress(data : String, level : Int32 = 6) : String\n # Every member of the file, one after another; bytes after the last\n # that do not start another are left as zlib leaves them.\n #\n # The last member's trailer names its length, which for a file of one\n # member is the length of the answer, so that is the size the output\n # starts at; a file of several grows past it by doubling. Every member\n # had been sized on its own, by the last trailer or by three times the\n # bytes after it, so each allocated what the rest of the file could\n # hold, and a hundred two-byte members ahead of one of 64 MB took 2.5 s\n # to read where Python's gzip took 0.15.\n def self.decompress(data : String) : String\n def initialize\nend\n\n# A compressed stream read a piece at a time: the compressed bytes come\n# from the block, the decompressed ones go out through `read`, and memory\n# stays at the 32 KiB window and what is waiting to be read.\n#\n# ```\n# body = Inflater.new(Envelope::Gzip) { socket_read }\n# while (piece = body.read(65536)!) != \"\"\n# ...\n# end\n# ```\n#\n# `Inflater.flushed` is the same decoder for a raw stream that comes as\n# pieces each ending between blocks - zlib's sync flush, what each\n# `Deflater#compress` ends with - and is decoded a piece at a time, the\n# window kept from one to the next: RFC 7692's permessage-deflate, whose\n# messages are such pieces with the flush's last four bytes left off.\npub class Inflater\n # Whether the stream has ended, its trailer checked, and every byte been\n # read: the next `read` answers \"\".\n def finished? : Bool\n # A raw stream decoded by `inflate`, a piece at a time.\n def self.flushed : Inflater\n # The next piece of a `flushed` stream, decoded whole: the blocks it\n # holds, which may refer back to the 32 KiB before it, and may be final\n # (RFC 7692 §7.2.1 lets a final block be followed by more). A piece\n # that does not end between blocks, or is not DEFLATE, is a\n # `CompressError`, and so is every piece after it. More than *limit*\n # bytes out is nil, found as they come - a piece that would decode to\n # gigabytes stops 64 KiB past *limit* - and fails the stream the same\n # way. On another `Inflater`, a panic.\n def inflate(piece : String, limit : Int32 = 2147483647) : String | CompressError | Nil\n # *source* answers the next bytes of the compressed stream, nil (or \"\")\n # when there are no more. It is called only when the decoder has no\n # bytes in hand for what it must read next, so it is never asked for\n # anything past the end of the stream - except that a gzip stream ends\n # only where no member follows, which takes a look at the next two\n # bytes.\n def initialize(envelope : Envelope, &source : (-> String | ::Nil))\n def initialize(core : Inflate)\n # Bytes the source gave beyond the end of the stream (gzip: after the\n # last member), for a caller that frames other data after it; \"\" before\n # the stream has ended.\n def leftover : String\n # Up to *max* decompressed bytes; \"\" once the stream and its trailer\n # (CRC-32 and length, or Adler-32) have ended and been checked. A read\n # answers as soon as it has bytes and going on would call the source,\n # so it can answer fewer than *max* before the end; and never more than\n # 64 KiB less a match at once, the room ahead of the window.\n #\n # A corrupt stream, a bad checksum, or input that ends early is a\n # `CompressError`, after which every read answers the same error. What\n # the same read decoded before the error is not handed out. A *max*\n # below 1 is a panic.\n def read(max : Int32 = 65536) : String | CompressError\nend\n\npub struct Zlib\n # CMF 0x78 (deflate, a 32 KiB window), FLG with the level's band and the\n # check bits that make the pair a multiple of 31 (no dictionary), then\n # the Adler-32 of the bytes, big-endian. *level* as `Deflate.compress`.\n def self.compress(data : String, level : Int32 = 6) : String\n def self.decompress(data : String) : String\n def self.hex(w : UInt32) : String\n def initialize\nend\n\nimpl Error for CompressError\n def message : String\nend\n" }, { "name": "std/cookie", "path": "src/std/cookie.iyi", - "sourceSha256": "0218a073aba1d8715700b1935bd4dd8fa6c86ce4748c71d0af43c9931ae7043f", + "sourceSha256": "bbcd3942b912a4a0389212bd87f75b15fd9f56f27daaec472a6d63d8e9b4c3eb", "surface": "module std/cookie\n\n# A cookie (RFC 6265): what a `Set-Cookie` line says, and what a jar\n# keeps. `Cookie.parse(line)` reads a `Set-Cookie` value as RFC 6265 §5.2\n# has a user agent read one - a name and value, then attributes in any\n# case, an unknown attribute ignored - and answers nil for a line that\n# has no name-value pair. `to_set_cookie` writes one, refusing a name or\n# value that would break the line. `expires` is the instant it lapses,\n# from `Max-Age` before `Expires` (§5.3, step 3); nil is a session cookie.\npub class Cookie\n def domain : String | ::Nil\n # Whether the cookie has lapsed at *now*.\n def expired?(now : Time) : Bool\n def expires : Time | ::Nil\n # The cookies an answer's `Set-Cookie` lines set, a line each.\n def self.from_response(response : Response) : Array(Cookie)\n def http_only : Bool\n def initialize(name : String, value : String, path : String | ::Nil = nil, domain : String | ::Nil = nil, expires : Time | ::Nil = nil, max_age : Int64 | ::Nil = nil, secure : Bool = false, http_only : Bool = false, same_site : String | ::Nil = nil)\n def max_age : Int64 | ::Nil\n def name : String\n def self.parse(line : String) : Cookie | ::Nil\n # The pairs of a request's `Cookie` header: `a=1; b=2`.\n def self.parse_header(value : String) : Hash(String, String)\n def path : String | ::Nil\n def same_site : String | ::Nil\n # `Max-Age`'s digits, with a sign (§5.2.2); nil for anything else.\n protected def self.seconds(text : String) : Int64 | ::Nil\n def secure : Bool\n # The `Set-Cookie` value a server writes (§4.1). A name is a token, and a\n # value the cookie-octets: a space, a comma, a semicolon, a backslash or\n # a quote inside one would end it or another field early.\n def to_set_cookie : String\n def value : String\nend\n\n# The cookies a client keeps between requests (RFC 6265 §5.3, §5.4): what\n# each answer's `Set-Cookie` lines set, by domain and path, and what a\n# request to a URL sends back. A cookie without `Domain` goes back to its\n# own host alone; one with a `Domain` the host is not within is refused,\n# and so is a `Domain` of one label, `com` - there is no public-suffix\n# list here, so a cookie for `co.uk` would be taken, which a browser would\n# refuse. A cookie that has lapsed, or is set lapsed, is gone. A `Secure`\n# cookie is taken from an `https://` answer only, and goes back over\n# `https://` only (§5.4 step 1). A jar may be shared by tasks on one\n# thread, not across threads.\npub class CookieJar\n # *cookie*, as set by an answer from *url*: it replaces one of the same\n # name, domain and path, and one set already lapsed removes it.\n def add(url : String, cookie : Cookie) : Nil\n # The cookies a request to *url* carries: domain and path matching,\n # unexpired, `Secure` only over `https://`; longer paths first, then\n # older (§5.4, step 2).\n def cookies_for(url : String) : Array(Cookie)\n # §5.1.4: the request path up to its last `/`, or `/`.\n protected def self.default_path(path : String) : String\n # §5.1.3: the host is the domain, or ends with it after a dot, and is\n # not an IP address.\n protected def self.domain_match?(host : String, domain : String) : Bool\n # The `Cookie` header for a request to *url*, nil when nothing goes.\n def header_for(url : String) : String | ::Nil\n def initialize\n # §5.1.4.\n protected def self.path_match?(request : String, cookie : String) : Bool\n # Whether *uri* is over a secure protocol: `https://` (and `wss://`).\n protected def self.secure?(uri : URI) : Bool\n def size : Int32\n # The `Set-Cookie` lines of *response*, from *url*, taken in.\n def update(url : String, response : Response) : Nil\nend\n\nimpl Std::Http::CookieStore for CookieJar\nend\n" }, { "name": "std/crypto", "path": "src/std/crypto.iyi", - "sourceSha256": "1e1376924f69f5b29c8fbda357872a3bceb3a85e9d0227c950ba6ef699b8473a", + "sourceSha256": "a6c0ecdda675af34c5ad2f32ca2b5f836d50aab1c050ccf7fc19f34e8f8602e7", "surface": "module std/crypto\n\n# HKDF, RFC 5869: `extract` concentrates input keying material into a\n# pseudorandom key, `expand` stretches that key into as many bytes as are\n# asked for, bound to an `info` that names their use; `derive` is both.\n# An empty salt is the RFC's default, a hash's length of zeros, which is\n# the same key to HMAC.\npub struct HKDF\n def self.derive(algorithm : H.class, ikm : String | Array(UInt8), salt : String | Array(UInt8), info : String | Array(UInt8), length : Int32) : Array(UInt8) forall H\n # At most 255 hashes' worth of bytes, which is what a one-byte block\n # counter can number.\n def self.expand(algorithm : H.class, prk : String | Array(UInt8), info : String | Array(UInt8), length : Int32) : Array(UInt8) forall H\n def self.extract(algorithm : H.class, salt : String | Array(UInt8), ikm : String | Array(UInt8)) : Array(UInt8) forall H\n def initialize\nend\n\npub class HMAC\n def self.digest(algorithm : H.class, key : String | Array(UInt8), data : String | Array(UInt8)) : Array(UInt8) forall H\n # Bytes in a MAC: the hash's digest size.\n def digest_size : Int32\n # The MAC, into the first `digest_size` bytes of *dst*, with nothing\n # allocated.\n def final(dst : Array(UInt8)) : Nil\n def final : Array(UInt8)\n def self.hexdigest(algorithm : H.class, key : String | Array(UInt8), data : String | Array(UInt8)) : String forall H\n def hexfinal : String\n def initialize(algorithm : H.class, key : String | Array(UInt8)) forall H\n # Ready for the next message under the same key.\n def reset : Nil\n def update(data : Pointer(UInt8), count : Int32) : self\n def update(data : String | Array(UInt8)) : self\nend\n\n# PBKDF2, RFC 8018 §5.2, with HMAC as its pseudorandom function: a key\n# from a password, made deliberately slow by *iterations*. OWASP's figure\n# for HMAC-SHA256 is 600,000.\npub struct PBKDF2\n def self.derive(algorithm : H.class, password : String | Array(UInt8), salt : String | Array(UInt8), iterations : Int32, length : Int32) : Array(UInt8) forall H\n def initialize\nend\n\npub struct Subtle\n # Whether *a* and *b* hold the same bytes, in a time that depends on\n # their sizes and not on their contents: every byte is read, and the\n # differences are gathered with `|` rather than tested one at a time.\n # Byte strings of different sizes answer false at once, so the size is\n # not a secret this keeps.\n def self.constant_time_compare(a : String | Array(UInt8), b : String | Array(UInt8)) : Bool\n def initialize\nend\n" }, { "name": "std/csv", "path": "src/std/csv.iyi", - "sourceSha256": "25190d666cdcf9fbb325221a59be13fff7a6da56a199fb44916523e58719bee6", + "sourceSha256": "60418210c97b24d0ecfc98edf6ed84280d3a10f034aed964f0d145e83c95cca8", "surface": "module std/csv\n\npub struct CSV\n # Into one builder: adding each field to the text so far copied that\n # text once per field, and 20,000 rows of five took 12.8 seconds.\n def self.build(rows : Array(Array(String))) : String\n def initialize\n def self.parse(text : String) : Array(Array(String))\nend\n\n# What is wrong with a document, and where: *line* and *column* count from\n# 1, the column in characters.\npub struct CSVError\n def column : Int32\n def initialize(message : String, line : Int32, column : Int32)\n def line : Int32\n def to_s : String\nend\n\n# Rows read one at a time from a `String` or an `IyiIO`.\n#\n# ```\n# reader = CSVReader.new(File.open(\"people.csv\"), headers: true)\n# while row = reader.next.or_panic\n# puts row[\"name\"]\n# end\n# ```\npub class CSVReader\n # Each row left, in order; the error that stopped them, if one did.\n def each(& : (CSVRow -> Nil)) : Nil | CSVError\n # The header row, read now if no row has been. Only for a reader made\n # with `headers: true`: on any other, the first row is a row.\n def headers : Array(String) | CSVError\n def initialize(text : String, separator : Char = ',', quote : Char = '\"', headers : Bool = false, strip : Bool = false, same_size : Bool = false)\n # Reads *io* as far as each row needs, CHUNK bytes at a time; it is not\n # closed.\n def initialize(io : IyiIO, separator : Char = ',', quote : Char = '\"', headers : Bool = false, strip : Bool = false, same_size : Bool = false)\n # The next row with its header's names, nil past the last.\n def next : CSVRow | CSVError | Nil\n # The next row's fields, nil past the last, or what is wrong with the\n # document there. A blank line is a row of no fields, except under a\n # header, where it is passed over.\n def next_row : Array(String) | CSVError | Nil\nend\n\n# One row as `CSVReader#next` gives it: its fields, by position or by the\n# header's names.\npub class CSVRow\n def [](at : Int32) : String\n # The field under the header *name*: \"\" when this row is too short to\n # have one, which is the document's doing. A name the header does not\n # have is the program's, and panics.\n def [](name : String) : String\n def []?(at : Int32) : String | ::Nil\n # The field under the header *name*, or nil when the header has no such\n # name or this row is too short to reach it.\n def []?(name : String) : String | ::Nil\n def fields : Array(String)\n # The header row, or none when the reader was made without one.\n def headers : Array(String)\n def initialize(fields : Array(String), headers : Array(String), index : Hash(String, Int32), line : Int32)\n # The line the row starts on.\n def line : Int32\n def size : Int32\n # The header's names to this row's fields, as far as both go.\n def to_h : Hash(String, String)\nend\n\n# Rows written to an `IyiIO`, quoted where a reader needs them to be.\n#\n# ```\n# out = CSVWriter.new(STDOUT, line_end: \"\\r\\n\")\n# out.row([\"name\", \"says\"])\n# out.row([\"Ada\", \"\\\"hello\\\", she said\"])\n# ```\npub class CSVWriter\n def flush : Nil\n def initialize(io : IyiIO, separator : Char = ',', quote : Char = '\"', quote_all : Bool = false, line_end : String = \"\\n\")\n # One row, and its line end.\n def row(fields : Array(String)) : Nil\n # The rows written so far.\n def rows : Int32\nend\n\nimpl Error for CSVError\n def message : String\nend\n" }, { "name": "std/curve25519", "path": "src/std/curve25519.iyi", - "sourceSha256": "b1b7fdcd424af5a7d8484a2b984f886c45231a74f396566691a5976113c67132", + "sourceSha256": "1519c4e25ac7019bbe7888e727db316b884046ecfb81e17417108a00f1374414", "surface": "module std/curve25519\n\n# What is wrong with a peer's key: `message` is the sentence.\npub struct CurveError\n def initialize(message : String)\n def to_s : String\nend\n\n# Ed25519, RFC 8032 §5.1: the seed is the 32-byte private key; SHA-512 of\n# it gives the secret scalar (its first half, clamped) and the prefix the\n# per-message nonce is hashed from (its second half). Signing is\n# deterministic: one seed and one message give one signature.\npub struct Ed25519\n def initialize\n # The 32-byte public key of *seed*, RFC 8032 §5.1.5.\n def self.public_key(seed : String | Array(UInt8)) : String\n # The 64-byte signature of *message* under *seed*, RFC 8032 §5.1.6:\n # R = [r]B for r = SHA-512(prefix || M) mod L, then\n # S = (r + SHA-512(R || A || M) * s) mod L. The public key A is worked\n # out again from the seed, which costs a second multiplication of the\n # base point; the two are each a fixed-window constant-time one.\n def self.sign(seed : String | Array(UInt8), message : String | Array(UInt8)) : String\n # Whether *signature* is *public_key*'s signature of *message*, RFC 8032\n # §5.1.7 with the cofactorless equation (the header says why). False,\n # not a panic, for a key or signature of the wrong size, a key that is\n # not a canonical encoding of a point on the curve, and an S of L or\n # more.\n def self.verify(public_key : String | Array(UInt8), message : String | Array(UInt8), signature : String | Array(UInt8)) : Bool\nend\n\n# An Ed25519 public key held as a value, as a certificate carries one (RFC\n# 8410): the 32 bytes, checked once to be the canonical encoding of a point\n# on the curve, so a key read from outside that is not one is refused where\n# it is read rather than failing every signature later.\npub struct Ed25519PublicKey\n def bytes : Array(UInt8)\n # The key *bytes* encode, or a `CurveError`: not 32 bytes, or not a\n # canonical encoding of a point on the curve (RFC 8032 §5.1.3).\n def self.from_bytes(bytes : String | Array(UInt8)) : Ed25519PublicKey | CurveError\n def initialize(bytes : Array(UInt8))\n # Whether *signature* is this key's signature of *message*, as\n # `Ed25519.verify` answers it.\n def verify(message : String | Array(UInt8), signature : String | Array(UInt8)) : Bool\nend\n\n# X25519, RFC 7748 §5: the u-coordinate of a clamped scalar times a point\n# on Curve25519. The private key is any 32 bytes; it is clamped here\n# (bits 0 to 2 and 255 cleared, bit 254 set), so the caller need not.\npub struct X25519\n def initialize\n # The public key of *private_key*: its multiple of the base point, u = 9.\n # That point is edwards25519's B under RFC 7748 §4.1's map, which keeps\n # multiples, so the clamped scalar times B on the Edwards curve, by the\n # fixed-base table, gives the same u as the ladder from 9 does: 8\n # microseconds where the ladder took 20.\n def self.public_key(private_key : String | Array(UInt8)) : String\n # The secret *private_key* shares with the owner of *peer_public*. A\n # peer key that is not 32 bytes, and a secret of all zeros (a peer key of\n # small order, RFC 7748 §6.1), are a `CurveError`. Whether the secret was\n # zero is the one thing that leaves the ladder by a branch, and it says\n # nothing of the private key: which keys give zero depends on the peer's.\n def self.shared(private_key : String | Array(UInt8), peer_public : String | Array(UInt8)) : String | CurveError\nend\n\nimpl Error for CurveError\n def message : String\nend\n" }, { "name": "std/debug", "path": "src/std/debug.iyi", - "sourceSha256": "21ad11724eee0f9a4e3c59e2684abf48702f9456a89b7cdf3e4bac543d223965", + "sourceSha256": "64e05b3d51e17dc9b6e49544e5adfe4ea5908d1ed1f2e6a2439095c62f836e94", "surface": "module std/debug\n" }, { "name": "std/deque", "path": "src/std/deque.iyi", - "sourceSha256": "3c10d9b4c1cb53f818d290d48773cb05cc5138909db71a1492dc0c74dee8c21e", + "sourceSha256": "3ee029b104c7f9f58d9747481efde5e387c850ebd35a435c9a34325d2f7abd4c", "surface": "module std/deque\n\npub class Deque(T)\n # A new deque: this one's elements, then other's.\n def +(other : Deque(T)) : Deque(T)\n def <<(value : T) : Deque(T)\n # The same elements in the same order, where one element type holds the\n # other, as tuples compare (src/iyi/object.iyi). Restricted to\n # `Deque(T)`, a `Deque(Int32)` was taken for a `Deque(Int32?)` and not\n # back: the one equality answered true one way and false the other.\n def ==(other : Deque(U)) : Bool forall U\n def []=(index : Int32, value : T) : T\n # Slots allocated, live or not.\n def capacity : Int32\n # Forgets every element; the capacity stays.\n def clear : Deque(T)\n # Appends every element of other; answers the deque. The count is read\n # once: walking `other.size` while pushing grew it along with this deque\n # when the two were one, so `d.concat(d)` never ended.\n def concat(other : Deque(T)) : Deque(T)\n def concat(items : Array(T)) : Deque(T)\n # Removes and answers the element at index; the shorter side closes\n # the gap.\n def delete_at(index : Int32) : T\n # A copy with the same elements, contiguous from slot 0.\n def dup : Deque(T)\n # size copies of value.\n def initialize(size : Int32, value : T)\n # An empty deque with room for initial_capacity elements before it grows.\n def initialize(initial_capacity : Int32)\n # An empty deque; the first push allocates.\n def initialize\n # Inserts value before index; `size` appends, `-1` appends too (the\n # position after the last). The shorter side moves.\n def insert(index : Int32, value : T) : Deque(T)\n def inspect : String\n # The array's elements, in its order.\n def self.new(items : Array(T)) : Deque(T)\n # size elements, each what the block answers for its index.\n def self.new(size : Int32, & : (Int32 -> T)) : Deque(T)\n # Removes the last n elements, or all of them when there are fewer.\n def pop(n : Int32) : Nil\n # Removes and answers the last element.\n def pop : T\n def pop? : T | ::Nil\n # Appends value; answers the deque.\n def push(value : T) : Deque(T)\n # Rotates in place so that the element at position n comes first; a\n # negative n rotates the other way. A full buffer only moves `@start`;\n # otherwise the shorter end is walked around.\n def rotate_in_place(n : Int32 = 1) : Deque(T)\n # A copy rotated by n, this deque untouched.\n def rotated(n : Int32 = 1) : Deque(T)\n # Removes the first n elements, or all of them when there are fewer.\n def shift(n : Int32) : Nil\n # Removes and answers the first element.\n def shift : T\n def shift? : T | ::Nil\n def size : Int32\n # `Deque{1, 2, 3}`, elements inspected.\n def to_s : String\n # The element at a logical index, no bounds check; `Indexable#[]` does\n # the checking.\n def unsafe_fetch(index : Int32) : T\n # Prepends value; answers the deque.\n def unshift(value : T) : Deque(T)\nend\n\nimpl Std::Indexable::Indexable for Deque(T) forall T\nend\n\nimpl Std::Indexable::IndexableMutable for Deque(T) forall T\n def unsafe_put(index : Int32, value : T) : T\nend\n\nimpl Std::Enumerable::Enumerable for Deque(T) forall T\nend\n" }, { @@ -160,31 +160,31 @@ { "name": "std/digest", "path": "src/std/digest.iyi", - "sourceSha256": "3a86961eb013ee516204d7f8e65615cc592e1ee624e570d8f49345d00894ab26", + "sourceSha256": "78f1b6ed44aabbe2590c5709ec115b2ebc48ea6cac61cb30362b935ecf4c2d43", "surface": "module std/digest\n\n# The same convention as `CRC32`: `update(sum, data, count)` goes on from a\n# finished Adler-32, and 1 is the start (the Adler-32 of nothing), as in\n# zlib's `adler32(adler, buf, len)`.\npub struct Adler32\n def self.checksum(data : String) : UInt32\n def self.hexdigest(data : String) : String\n def initialize\n def self.update(sum : UInt32, data : Pointer(UInt8), count : Int32) : UInt32\nend\n\n# `checksum(data)` is the CRC-32 of *data*. `update(crc, data, count)` goes\n# on from a CRC-32 already taken, zlib's `crc32(crc, buf, len)` convention:\n# what goes in and what comes out are finished checksums, the inversion\n# before and after done inside, so 0 is the start (the CRC-32 of nothing)\n# and `update(update(0, a), b)` is the CRC-32 of *a* then *b*, the same\n# number `checksum` answers for the two joined.\npub struct CRC32\n def self.checksum(data : String) : UInt32\n def self.hexdigest(data : String) : String\n def initialize\n # Eight bytes a step through eight tables (slicing-by-8), where it had\n # been one bit a step: twenty megabytes took 105 ms, which was most of\n # what `Gzip.decompress` spent, the inflating itself taking 30.\n def self.update(crc : UInt32, data : Pointer(UInt8), count : Int32) : UInt32\nend\n\n# CRC-64 with ECMA-182's polynomial, reflected (0xc96c5795d7870f42), and\n# inverted before and after: the CRC-64 the .xz format checks a block\n# with (xz file format 1.0.4 §3.4, check ID 0x04), which reveng's\n# catalogue names CRC-64/XZ. The same convention as `CRC32`: `update(crc,\n# data, count)` goes on from a finished checksum, 0 is the start (the\n# CRC-64 of nothing), and `hexdigest` writes the number most significant\n# digit first, as `xz --robot --list` does; the stream stores it\n# little-endian.\npub struct CRC64\n def self.checksum(data : String) : UInt64\n def self.hexdigest(data : String) : String\n def initialize\n # Eight bytes a step through eight tables, as `CRC32` goes: the bytes\n # loaded as one little-endian word, xored into the checksum, and each\n # of the eight looked up in the table for how many bytes of zeros\n # follow it in the step.\n def self.update(crc : UInt64, data : Pointer(UInt8), count : Int32) : UInt64\nend\n\n# What every block hash shares: the message is cut into blocks, the last\n# one padded with a one bit, zeros and the message's length in bits, and\n# each block is compressed into the state. A subclass is the state and\n# its compression; this is the cutting and the padding.\npub abstract class Digest\n # Bytes compressed at a time: 64, or 128 for SHA-384 and SHA-512. HMAC\n # pads its key to this.\n def block_size : Int32\n # Becomes *other*: the same bytes taken, the same state, the same\n # `final` given or not. Nothing is allocated. *other* must be the same\n # algorithm, and a different one is refused by name.\n def copy_from(other : Digest) : Nil\n # Bytes in a digest: 16 for MD5, 20 for SHA-1, 28, 32, 48 and 64 for\n # the SHA-2s.\n abstract def digest_size : Int32\n def dup : self\n # The digest, into the first `digest_size` bytes of *dst*: no array is\n # made, which is what a loop that hashes a hash wants.\n def final(dst : Array(UInt8)) : Nil\n def final : Array(UInt8)\n def self.hash_bytes(data : String | Array(UInt8)) : Array(UInt8)\n def self.hexdigest(data : String | Array(UInt8)) : String\n def hexfinal : String\n def initialize(block_size : Int32)\n # The digest of no bytes yet, ready again after a `final`.\n def reset : Nil\n def update(data : Pointer(UInt8), count : Int32) : self\n def update(data : String) : self\n def update(data : Array(UInt8)) : self\nend\n\npub class MD5 < Digest\n def digest_size : Int32\n def initialize\n # The type's own name, which is what a value that has said nothing about\n # itself can honestly print. A type that wants better defines its own.\n def to_s : String\nend\n\npub class SHA1 < Digest\n def digest_size : Int32\n def initialize\n # The type's own name, which is what a value that has said nothing about\n # itself can honestly print. A type that wants better defines its own.\n def to_s : String\nend\n\npub class SHA224 < SHA256\n def digest_size : Int32\n # The type's own name, which is what a value that has said nothing about\n # itself can honestly print. A type that wants better defines its own.\n def to_s : String\nend\n\n# SHA-256, FIPS 180-4 §6.2. SHA-224 is the same compression from another\n# starting state, its digest the first 28 bytes.\npub class SHA256 < Digest\n def digest_size : Int32\n def initialize\n # The type's own name, which is what a value that has said nothing about\n # itself can honestly print. A type that wants better defines its own.\n def to_s : String\nend\n\n# SHA-3, FIPS 202 section 6.1: Keccak[c] with a capacity twice the\n# digest, so a rate (the `block_size`) of 200 bytes less two digests.\n# The sponge has no length field, so the base's cutting into blocks is\n# kept and its padding is replaced. The digest is shorter than the rate\n# in all four, so it is the first bytes of the state after the last\n# block, with no further permutation.\npub abstract class SHA3 < Digest\n def initialize(rate : Int32)\n # The type's own name, which is what a value that has said nothing about\n # itself can honestly print. A type that wants better defines its own.\n def to_s : String\nend\n\npub class SHA384 < SHA512\n def digest_size : Int32\n # The type's own name, which is what a value that has said nothing about\n # itself can honestly print. A type that wants better defines its own.\n def to_s : String\nend\n\npub class SHA3_224 < SHA3\n def digest_size : Int32\n def initialize\n # The type's own name, which is what a value that has said nothing about\n # itself can honestly print. A type that wants better defines its own.\n def to_s : String\nend\n\npub class SHA3_256 < SHA3\n def digest_size : Int32\n def initialize\n # The type's own name, which is what a value that has said nothing about\n # itself can honestly print. A type that wants better defines its own.\n def to_s : String\nend\n\npub class SHA3_384 < SHA3\n def digest_size : Int32\n def initialize\n # The type's own name, which is what a value that has said nothing about\n # itself can honestly print. A type that wants better defines its own.\n def to_s : String\nend\n\npub class SHA3_512 < SHA3\n def digest_size : Int32\n def initialize\n # The type's own name, which is what a value that has said nothing about\n # itself can honestly print. A type that wants better defines its own.\n def to_s : String\nend\n\n# SHA-512, FIPS 180-4 §6.4: 64-bit words, 128-byte blocks, 80 rounds.\n# SHA-384 is the same compression from another starting state, its digest\n# the first 48 bytes.\npub class SHA512 < Digest\n def digest_size : Int32\n def initialize\n # The type's own name, which is what a value that has said nothing about\n # itself can honestly print. A type that wants better defines its own.\n def to_s : String\nend\n\npub class SHAKE128 < Shake\n def initialize\n # The type's own name, which is what a value that has said nothing about\n # itself can honestly print. A type that wants better defines its own.\n def to_s : String\nend\n\npub class SHAKE256 < Shake\n def initialize\n # The type's own name, which is what a value that has said nothing about\n # itself can honestly print. A type that wants better defines its own.\n def to_s : String\nend\n\n# SHAKE128 and SHAKE256, FIPS 202 section 6.2: the sponge at a capacity of\n# 256 and 512 bits, as much output as is asked for. `update` absorbs;\n# the first `read` pads and every `read` squeezes on from where the last\n# stopped, so reads of 3 and 5 bytes are the first 8 bytes of one read.\n# Absorbing after squeezing would be a different function, not more\n# input, and is refused until `reset`.\npub abstract class Shake\n # Bytes absorbed at a time: 168 for SHAKE128, 136 for SHAKE256.\n def block_size : Int32\n # *count* bytes of the output for *data*.\n def self.hash_bytes(data : String | Array(UInt8), count : Int32) : Array(UInt8)\n def self.hexdigest(data : String | Array(UInt8), count : Int32) : String\n def initialize(rate : Int32)\n # The next `dst.size` bytes of output into *dst*, with no array made.\n # While squeezing, `@fill` counts the bytes of the state given out.\n def read(dst : Array(UInt8)) : Nil\n def read(count : Int32) : Array(UInt8)\n def reset : Nil\n def update(data : Pointer(UInt8), count : Int32) : self\n def update(data : String) : self\n def update(data : Array(UInt8)) : self\nend\n\n# XXH64, the 64-bit xxHash (Yann Collet; the algorithm as xxhash's\n# `doc/xxhash_spec.md` gives it): the hash whose low 32 bits are a\n# Zstandard frame's content checksum (RFC 8878 §3.1.1), which is what\n# std/zstd takes it for. Not a cryptographic hash - a fast check that\n# bytes arrived as they left, with nothing to keep anyone from making two\n# messages that collide.\n#\n# `checksum(data, seed)` hashes one piece. `new(seed)`, then `update` as\n# the data arrives, then `digest`, hashes it in pieces; `digest` leaves\n# the state as it was, as xxhash's `XXH64_digest` does, so it can be\n# asked midway and the hash go on. `hexdigest` is xxhash's canonical form,\n# most significant byte first. A word of the message is loaded as one\n# little-endian load, which is what every target iyi builds for is.\npub class XXH64\n def self.checksum(data : String, seed : UInt64 = 0_u64) : UInt64\n # The hash of everything given since the start or `reset`; the state is\n # left as it was.\n def digest : UInt64\n def self.hexdigest(data : String, seed : UInt64 = 0_u64) : String\n def initialize(seed : UInt64 = 0_u64)\n def self.merge(acc : UInt64, v : UInt64) : UInt64\n # Back to the hash of nothing, under the same seed.\n def reset : Nil\n def self.rotl(x : UInt64, n : Int32) : UInt64\n def self.round(acc : UInt64, input : UInt64) : UInt64\n def update(data : Pointer(UInt8), count : Int32) : Nil\n def update(data : String) : Nil\nend\n" }, { "name": "std/dir", "path": "src/std/dir.iyi", - "sourceSha256": "05d924d2c84ce21c8c566beaa058822b50fb1a4e5547b588a111f7085dbd2356", + "sourceSha256": "b0c2c4f708c001f4ff74f8ef696e06b5f58c2027b88c2604e9067de48b261771", "surface": "module std/dir\n\npub class Dir\n def self.[](patterns : String | Array(String)) : Array(String)\n protected def self.c_str(str : String) : ::Pointer(UInt8)\n def self.cd(path : String | Path) : String\n def self.cd(path : String | Path, &block : (-> Nil)) : Nil\n def children : Array(String)\n def self.children(dirname : String | Path) : Array(String)\n def close : Nil\n def closed? : Bool\n # One implementation, in the prelude: `Path#expand` needs the same\n # answer and cannot import this module (`std/dir` imports `std/path`),\n # so the platform arms live beside `Program.args` and `Program.env`,\n # which are the process's other facts.\n def self.current : String\n def self.current=(path : String | Path) : String\n def self.delete(path : String | Path) : Nil\n def self.delete?(path : String | Path) : Bool\n def each(&block : (String -> Nil)) : Nil\n def self.each(dirname : String | Path, &block : (String -> Nil)) : Nil\n def each_child(&block : (String -> Nil)) : Nil\n def self.each_child(dirname : String | Path, &block : (String -> Nil)) : Nil\n def self.empty?(path : String | Path) : Bool\n def entries : Array(String)\n def self.entries(dirname : String | Path) : Array(String)\n def self.exists?(path : String | Path) : Bool\n def self.glob(patterns : String | Array(String)) : Array(String)\n def self.glob(patterns : String | Array(String), &block : (String -> Nil)) : Nil\n def initialize(path : String | Path)\n def inspect : String\n def self.mkdir(path : String | Path, mode : Int32 = 511) : Nil\n def self.mkdir_p(path : String | Path, mode : Int32 = 511) : Nil\n def self.open(path : String | Path) : Dir\n def self.open(path : String | Path, &block : (Dir -> Nil)) : Nil\n def path : String\n def read : String | ::Nil\n def rewind : self\n def self.rmdir(path : String | Path) : Nil\n def self.tempdir : String\n def to_s : String\n # One trailing separator off, as Crystal's `Dir.tempdir` answers: darwin\n # sets TMPDIR to `/var/folders/.../T/`, and every path joined onto it had\n # two separators and a `dirname` that was not the directory it came from.\n # The root alone is left as it is - and on Windows a drive's root is\n # `C:\\`, whose separator is the root: without it `C:` is the current\n # directory on that drive, and a TMP of `C:\\` answered the directory\n # the program happened to be in.\n def self.without_trailing_separator(dir : String) : String\nend\n\n# Linux: the kernel's own calls (SPEC.md III.9), in libc's shape so the\n# callers below read the same. A directory stream is a descriptor and\n# a buffer of `linux_dirent64` records from `getdents64`, which is the\n# record libc's `readdir` hands back: the name sits at byte 19 either way.\npub struct LibC\n def self.chdir(path : ::Pointer(UInt8)) : Int32\n def self.closedir(dirp : ::Pointer(Void)) : Int32\n def self.getcwd(buf : ::Pointer(UInt8), size : UInt64) : ::Pointer(UInt8)\n def initialize\n def self.mkdir(path : ::Pointer(UInt8), mode : Int32) : Int32\n def self.opendir(name : ::Pointer(UInt8)) : ::Pointer(Void)\n def self.readdir(dirp : ::Pointer(Void)) : ::Pointer(Void)\n def self.rewinddir(dirp : ::Pointer(Void)) : Nil\n def self.rmdir(path : ::Pointer(UInt8)) : Int32\n def self.sys(number : Int64, a : Int64, b : Int64, c : Int64) : Int64\nend\n" }, { "name": "std/dns", "path": "src/std/dns.iyi", - "sourceSha256": "bb493ba2b8a8be515c2c05d1f388ea1650a49d239c9f7973483a16b1e85f06ae", + "sourceSha256": "c46ce5eb44ce7ce573761ce81f50c1eafe60fec972ed69dacfd1f08b2b1d099a", "surface": "module std/dns\n\n# Why a name was not resolved. One type with a question per answer, as\n# `SocketError` has, so a caller tells a name that does not exist\n# (`not_found?`, NXDOMAIN - authoritative, not worth asking again) from a\n# server that failed (`server_failure?`, SERVFAIL - maybe worth asking\n# again later) without reading a sentence.\npub struct DnsError\n # The same failure, said about *name*: a failure found while reading\n # bytes knows no name, and the resolver that asked does.\n def about(name : String, server : String) : DnsError\n def detail : String\n def initialize(kind : Int32, name : String, server : String = \"\", detail : String = \"\")\n # Not a host name at all, so nothing was asked.\n def invalid_name? : ::Bool\n def kind : Int32\n # An answer that carried the query's ID and question and could not be\n # read: a compression loop, a pointer past the end, a record running off\n # the message, an alias chain that does not end.\n def malformed? : ::Bool\n # The name as the caller wrote it.\n def name : String\n # The name exists and has no address of the kind asked for (NOERROR\n # with no records, \"NODATA\").\n def no_address? : ::Bool\n # NXDOMAIN: the name does not exist.\n def not_found? : ::Bool\n def reason : String\n # REFUSED, FORMERR or NOTIMP: the server would not answer this query.\n def refused? : ::Bool\n # The server whose answer this is, `address:port`; empty when no server\n # was asked.\n def server : String\n # SERVFAIL from every server asked.\n def server_failure? : ::Bool\n # No server answered within the timeout, on any attempt.\n def timed_out? : ::Bool\n # The server could not be reached: nothing listens on its port (an ICMP\n # refusal), no route to it, or the TCP connection the truncated answer\n # needed failed.\n def unreachable? : ::Bool\nend\n\n# A query, and the reading of an answer to one.\npub struct DnsMessage\n # The addresses of *type* this answer gives *name*, following CNAMEs:\n # the records for the end of the chain, the name the chain ended at, and\n # how many links it took. No addresses and a different name is a chain\n # that left this answer before it ended, which the resolver asks about\n # next. A chain of more than *budget* links is malformed.\n def addresses(name : String, type : Int32, budget : Int32 = MAX_CNAMES) : Tuple(Array(Bytes), String, Int32) | DnsError\n def answers : Array(DnsRecord)\n # Whether this is the answer to query *id* for *name* and *type*: a\n # response, a standard query's, with the ID, and repeating the question.\n # A refusal of the query (SERVFAIL, REFUSED, FORMERR, NOTIMP) may come\n # without the question, as some servers send it; an answer that says\n # anything about the name - NOERROR or NXDOMAIN - must carry it.\n def answers?(id : UInt16, name : String, type : Int32) : ::Bool\n # Where the message at the start of *packet* ends: past its header, its\n # questions and every record of its three sections, as their counts\n # say; -1 when they run past the bytes or a name cannot be read. Over a\n # stream the frame is the message, so an answer whose frame holds bytes\n # past this point is refused as oversized there (`Resolver`'s encrypted\n # upstreams); a datagram is not held to it.\n def self.ends_at(packet : Bytes) : Int32\n def flags : Int32\n def id : UInt16\n # The ID in a datagram's first two bytes, or -1 when it is too short to\n # be a message: what the resolver reads before it reads anything else.\n def self.id_of(packet : Bytes) : Int32\n # *extended_rcode* is the upper eight bits of the RCODE, from the\n # answer's OPT record (RFC 6891 6.1.3); 0 without one.\n def initialize(id : UInt16, flags : Int32, question_name : String, question_type : Int32, question_class : Int32, answers : Array(DnsRecord), extended_rcode : Int32 = 0)\n def self.malformed(why : String) : DnsError\n def opcode : Int32\n # The message in *packet*: its header, its one question, and - unless it\n # is truncated - its answer section, and the RCODE's upper bits from the\n # OPT record in its additional section. The authority and additional\n # sections are walked to find that record, so their records must fit\n # the bytes too; one OPT record at most (RFC 6891 6.1.1), owned by the\n # root. Anything that does not fit the bytes is `DnsError` with\n # `malformed?`, never a read past them.\n def self.parse(packet : Bytes) : DnsMessage | DnsError\n # The query for *name*'s records of *type*, class IN, with recursion\n # desired: a 12-byte header and one question. *name* is a host name\n # (`DnsName.valid?`); a trailing dot is dropped.\n #\n # A *block* above 0 pads it (RFC 7830): an OPT record follows the\n # question - the root for its name, `EDNS_PAYLOAD` for its class,\n # extended RCODE, version and flags all 0 (RFC 6891 6.1.2-6.1.4) - and\n # holds a single Padding option of zero bytes (RFC 7830 3's SHOULD), as\n # many as bring the message to the next multiple of *block*. The length\n # brought there is the message's own, without the two bytes that frame\n # it over TCP (RFC 8467 3), so a question pads to the same length over\n # TLS as over HTTPS. With no *block* there is no OPT record: that is the\n # query that goes out in the clear, where RFC 7830 6 forbids padding.\n def self.query(id : UInt16, name : String, type : Int32, block : Int32 = 0) : Bytes\n def question_class : Int32\n # The question the answer repeats; empty with `question_type` 0 when it\n # repeats none.\n def question_name : String\n def question_type : Int32\n # The whole twelve-bit RCODE: the header's four bits under the OPT\n # record's eight. An answer that says BADVERS (16) has 0 in its header,\n # and read from the header alone it would be NOERROR without records.\n def rcode : Int32\n # The name at *at* in *packet*, and the offset just past it where it\n # sits; nil when it cannot be read. Labels are 1 to 63 bytes, a label may\n # not hold a `.` (it would read as two), the name is at most 255 bytes on\n # the wire, and each compression pointer must point before the place the\n # name was last read from - RFC 1035 4.1.4's \"prior occurrence\" - so the\n # positions strictly fall and a loop cannot be written.\n def self.read_name(packet : Bytes, at : Int32) : Tuple(String, Int32) | ::Nil\n def response? : ::Bool\n # TC: the answer did not fit in the datagram and is to be asked again\n # over TCP. Its records are not read: what is there is a fragment.\n def truncated? : ::Bool\nend\n\n# What a host name may be, and the names a short one stands for.\npub struct DnsName\n # The fully qualified names a lookup of *name* asks for, in order, as\n # resolv.conf(5) says: a name ending in `.` is only itself; one with at\n # least *ndots* dots is tried as it is first and then under each search\n # domain; one with fewer is tried under each search domain first and as\n # it is last. A candidate longer than a name can be is left out.\n def self.candidates(name : String, search : Array(String), ndots : Int32) : Array(String)\n def initialize\n # *name* without the trailing dot that makes it absolute.\n def self.relative(name : String) : String\n # Two names are the same name in any case (RFC 4343), with or without\n # the trailing dot.\n def self.same?(a : String, b : String) : ::Bool\n # A host name as RFC 1123 and glibc's `res_hnok` read one: labels of 1 to\n # 63 letters, digits, `-` and `_`, no label starting with `-`, at most\n # 253 characters, one trailing dot allowed. The last label must not be\n # all digits (RFC 3696 2: no top-level domain is), so `999.1.1.1` and\n # `1.2.3` stay what the numeric parser refuses rather than becoming\n # questions to a name server.\n def self.valid?(name : String) : ::Bool\n # Why *name* is not a host name, or nil when it is.\n def self.why(name : String) : String | ::Nil\nend\n\n# One resource record from an answer section.\npub struct DnsRecord\n # The character-strings of a TXT record's data (RFC 1035 3.3.14), each\n # a length byte and that many bytes; nil when the last one runs past the\n # data. A record holds one or more, and `Resolver#txt` joins them, as\n # SPF (RFC 7208 3.3) and DKIM read a record of several.\n def self.character_strings(data : Bytes) : Array(String) | ::Nil\n # The record's data as it came: four bytes for an A record, sixteen for\n # an AAAA, the encoded name for a CNAME.\n def data : Bytes\n def initialize(name : String, type : Int32, record_class : Int32, ttl : UInt32, data : Bytes, target : String)\n def name : String\n def record_class : Int32\n # A CNAME's target, read with compression; empty for any other type.\n def target : String\n def ttl : UInt32\n def type : Int32\nend\n\n# The hosts file: an address, then the names it answers for.\npub struct HostsFile\n def initialize\n # The addresses *text* gives *name*, as written and in the file's order:\n # every line whose names include it, in any case and with or without a\n # trailing dot. A line with an address and no name says nothing.\n def self.lookup(text : String, name : String) : Array(String)\nend\n\n# `/etc/resolv.conf`, as resolv.conf(5) describes it and glibc reads it.\npub struct ResolvConf\n def attempts : Int32\n def initialize(nameservers : Array(String) = [] of String, search : Array(String) = [] of String, ndots : Int32 = 1, timeout_ms : Int32 = 5000, attempts : Int32 = 2)\n # Each an address as written - IPv4, or IPv6 without a zone - optionally\n # with a port (`192.0.2.1:5353`, `[2001:db8::1]:5353`), which\n # resolv.conf itself never has and a resolver built in a program may.\n def nameservers : Array(String)\n def ndots : Int32\n # The keywords glibc reads: `nameserver`, `domain`, `search` (the later\n # of `domain` and `search` wins, as there) and `options` with `ndots:`,\n # `timeout:` (seconds) and `attempts:`, each held to glibc's bounds - 15,\n # 30 and 5. A `#` or `;` starts a comment. Anything else is ignored, as\n # glibc ignores it: a file other programs read too is not this module's\n # to refuse.\n def self.parse(text : String) : ResolvConf\n def search : Array(String)\n def timeout_ms : Int32\nend\n\n# What SVCB resolution (RFC 9460 section 3) of a service came to: the\n# ServiceMode records found, by priority, and the name they were asked\n# at - the service's own, or the target of the last AliasMode record\n# followed (`aliases` of them). No records is resolution that found no\n# endpoint; where `aliases` is above 0 the name is still one to connect\n# to (section 3's last endpoint), and \".\" says the service is not there\n# (section 2.5.1). `Resolver#https_records` answers one.\npub struct SvcbAnswer\n def aliases : Int32\n def initialize(name : String, aliases : Int32, records : Array(SvcbRecord))\n def name : String\n def records : Array(SvcbRecord)\n # An AliasMode record's target was \".\": the service says it is not there.\n def unavailable? : ::Bool\nend\n\n# One SVCB or HTTPS record's data (RFC 9460 section 2.2): its SvcPriority,\n# its TargetName, and its SvcParams by key.\n#\n# A priority of 0 is AliasMode - the service is at *target*'s records of\n# the same type, and the params mean nothing (section 2.4.2) - and any\n# other ServiceMode, *target* an endpoint and the params how to reach it,\n# a smaller priority preferred. The target is written out without its\n# trailing dot, and is \".\" for the root, which in ServiceMode stands for\n# the record's owner (`endpoint`, section 2.5.2) and in AliasMode says\n# the service is not there (section 2.5.1).\n#\n# What `parse` refuses as malformed, with `DnsError#malformed?`: what\n# section 2.2 has a client refuse - a param cut short by the end of the\n# data, keys out of strictly increasing order, a key twice - a target\n# that is compressed (it must not be, section 2.2) or holds a label with\n# a dot in it (see `DnsMessage.read_name`), and a value of a key it\n# reads that is not of its key's form (sections 7.1 to 7.4, and RFC 9848\n# 3's ech): mandatory a list of keys in increasing order that does not\n# list itself, alpn one or more ids of 1 to 255 bytes filling the value,\n# no-default-alpn empty, port two bytes, ipv4hint and ipv6hint one or\n# more addresses, ech an ECHConfigList whose length prefix is its own.\n# So is a record that is not self-consistent (section 2.4.3): a key\n# mandatory lists that it does not carry, no-default-alpn without alpn.\n# Section 2.2 then has the whole RRset refused, which `Resolver` does.\n# A key past ech is kept as its bytes, whatever they are.\n#\n# `to_s` is the record's data in presentation format as dig 9.20 prints it,\n# for reading the record while debugging: bench/std_dns_exercise.sh holds\n# the two to the same text, record by record.\npub struct SvcbRecord\n # AliasMode: the service is at the target's records of the same type.\n def alias? : ::Bool\n # The alpn-ids of `alpn` (section 7.1), in their order; none without it.\n def alpn : Array(String)\n # The ECHConfigList of `ech` (RFC 9848 3), length prefix and all, as\n # std/tls's `TlsConfig#ech_configs` takes it; nil without it.\n def ech : Bytes | ::Nil\n # The host a ServiceMode record's endpoint is: the target, or the\n # record's owner when the target is \".\" (section 2.5.2).\n def endpoint : String\n def initialize(owner : String, priority : Int32, target : String, keys : Array(Int32), values : Array(Bytes))\n # The addresses of `ipv4hint` and `ipv6hint` (section 7.3), four bytes\n # and sixteen each, as they came.\n def ipv4hint : Array(Bytes)\n # Sixteen bytes as BIND's inet_ntop writes them, which is RFC 5952's\n # form with one more rule: the longest run of two or more zero groups,\n # the first of equal ones, as `::`; and an address whose first six\n # groups are zero, or five zero and then ffff, ending in its last four\n # bytes dotted - `::1.2.3.4`, `::ffff:192.0.2.1` - though not `::` and\n # `::1`, whose run of zeros is longer.\n def self.ipv6_text(address : Bytes) : String\n def ipv6hint : Array(Bytes)\n # *key*'s name in presentation format: one of `NAMES`, or `keyNNNNN`\n # (section 2.1).\n def self.key_name(key : Int32) : String\n # The keys in their order, which is increasing, and each one's value as\n # it came.\n def keys : Array(Int32)\n # The keys `mandatory` lists (section 8); none without it.\n def mandatory : Array(Int32)\n # Whether the scheme's default protocols are left out of the record's\n # (section 7.1): http/1.1, for HTTPS.\n def no_default_alpn? : ::Bool\n # The name the record was found at: what a ServiceMode target of \".\"\n # stands for.\n def owner : String\n # The record in *data*, found at *owner*; or why it is malformed (see\n # the struct).\n def self.parse(data : Bytes, owner : String = \"\") : SvcbRecord | DnsError\n # The port the endpoint is reached on (section 7.2); nil for the\n # origin's own.\n def port : Int32 | ::Nil\n def priority : Int32\n # *data* in pieces of *width* bytes; none for nil.\n def self.split(data : Bytes | ::Nil, width : Int32) : Array(Bytes)\n def target : String\n # The record's data in presentation format (section 2.1), as dig 9.20\n # writes it: the priority; the target, absolute, its bytes escaped as\n # BIND escapes a name's; then each param in the order it came, by its\n # name - alpn's ids quoted and joined by commas, a comma in one written\n # `\\\\,` and a backslash `\\\\\\\\`; port in decimal; the hints as addresses,\n # IPv6 as BIND's inet_ntop writes one (`SvcbRecord.ipv6_text`); ech in\n # Base64; a key this module does not read as `keyNNNNN` and its value\n # quoted, or alone when it is empty. Inside quotes a byte outside\n # printable ASCII is `\\DDD`, in decimal; alpn's ids take a space so too,\n # and the generic form takes it as it is (dig 9.20.24, measured).\n def to_s : String\n # The value of *key*, or nil when the record does not carry it.\n def value(key : Int32) : Bytes | ::Nil\n def values : Array(Bytes)\n # One byte of an alpn-id, inside the quotes: the list's own escapes (RFC\n # 9460 appendix A.1) under the quoted string's.\n def self.write_alpn_byte(text : String::Builder, byte : UInt8) : Nil\n # *name*, absolute, as BIND writes one: `\"`, `(`, `)`, `;`, `\\`, `@`\n # and `$` after a backslash, a byte outside printable ASCII as `\\DDD`.\n def self.write_name(text : String::Builder, name : String) : Nil\n # One byte of a value in the generic form, inside the quotes.\n def self.write_quoted_byte(text : String::Builder, byte : UInt8) : Nil\nend\n\nimpl Error for DnsError\n def message : String\nend\n" }, { "name": "std/ecdsa", "path": "src/std/ecdsa.iyi", - "sourceSha256": "29ecb1c042308053d33ee2fc0289839f8a96ca4e5c6f36815c8f379fb3c57399", + "sourceSha256": "cf27f05f03926a1e3fda1e6589bafa58ddcb5d4c05118c52bebf319e7c07fa35", "surface": "module std/ecdsa\n\n# The curves a key can be on.\npub enum EcdsaCurve : Int32\n P256 = 0\n P384 = 1\n P521 = 2\n # The length of the group's order n in bits.\n def bits : Int32\n def self.from_oid(oid : String) : EcdsaCurve | ::Nil\n # The curve's OID, as a key's AlgorithmIdentifier names it.\n def oid : String\n # The bytes a coordinate, or a scalar, is written in.\n def size : Int32\nend\n\n# What reading a key answers for bytes that are not an ECDSA public key.\npub struct EcdsaError\n def initialize(message : String)\n def to_s : String\nend\n\n# The hashes a signature is made over.\npub enum EcdsaHash : Int32\n SHA256 = 0\n SHA384 = 1\n SHA512 = 2\n def digest(data : Array(UInt8)) : Array(UInt8)\nend\n\npub struct EcdsaPublicKey\n def curve : EcdsaCurve\n # The key from a point in SEC 1's uncompressed encoding (section 2.3.3):\n # 0x04, then x and y big-endian at the curve's width.\n def self.from_point(curve : EcdsaCurve, point : Array(UInt8)) : EcdsaPublicKey | EcdsaError\n # A SubjectPublicKeyInfo (RFC 5280 section 4.1.2.7) naming\n # id-ecPublicKey and one of the three curves (RFC 5480 section 2.1.1).\n def self.from_spki(der : Array(UInt8)) : EcdsaPublicKey | EcdsaError\n def self.from_spki(value : DerValue) : EcdsaPublicKey | EcdsaError\n def initialize(curve : EcdsaCurve, x : BigInt, y : BigInt, group : EcdsaGroup)\n # Whether *signature*, the DER of an Ecdsa-Sig-Value, is this key's\n # signature of *message* under *hash*.\n def verify(hash : EcdsaHash, message : Array(UInt8) | String, signature : Array(UInt8)) : Bool\n def x : BigInt\n def y : BigInt\nend\n\nimpl Error for EcdsaError\n def message : String\nend\n" }, { "name": "std/eiy", "path": "src/std/eiy.iyi", - "sourceSha256": "83471e251f8bb471753b29caae9b6e066c9a9bfe2ebb1471b2b426ee48ab0fdc", + "sourceSha256": "18106009c1b8945d5eb6019e3c22b59177dde9cc579444f38815b9bfb0c4ad88", "surface": "module std/eiy\n\npub struct Eiy\n # Collects what a template prints, for `render` or for a caller that wants\n # the text rather than a stream.\n class Buffer\n def initialize\n def print(text : String) : Nil\n def print(value) : Nil\n # `print`, the one verb a template's target has, so a Buffer can be one.\n def to_s(io) : Nil\n def to_s : String\n end\n class Lexer\n class Token\n enum Type : Int32\n String = 0\n Output = 1\n Control = 2\n EOF = 3\n end\n def column_number : Int32\n def column_number=(value : Int32) : Int32\n def control? : Bool\n def eof? : Bool\n def initialize\n def line_number : Int32\n def line_number=(value : Int32) : Int32\n def output? : Bool\n def string? : Bool\n def suppress_leading : Bool\n def suppress_leading=(value : Bool) : Bool\n def suppress_leading? : Bool\n def suppress_trailing : Bool\n def suppress_trailing=(value : Bool) : Bool\n def suppress_trailing? : Bool\n def type : Type\n def type=(value : Type) : Type\n def value : String\n def value=(value : String) : String\n end\n # Why the last `next_token?` answered nil, or nil while it has not.\n def error : String | ::Nil\n def initialize(string : String)\n # The next token; the same `Token` object is reused, so copy what is\n # needed before asking again. Panics on a tag that is never closed.\n def next_token : Token\n # The next token, or nil on a tag that is never closed; `error` then says\n # where.\n def next_token? : Token | ::Nil\n end\n def initialize\n # Where `embed` reads *template* from when it is written in *caller*: the\n # template itself when absolute, otherwise beside the caller.\n def self.locate(template : String, caller : String) : String\n # `process_string` of the file at *filename*.\n def self.process_file(filename : String, io_name : String = DefaultIoName) : String\n # `process_string?` of the file at *filename*.\n def self.process_file?(filename : String, io_name : String = DefaultIoName) : String | ::Nil\n # The iyi source that prints the template *string* to *io_name*. Panics on\n # a tag that is never closed, naming *filename* when there is one.\n def self.process_string(string : String, filename : String = \"\", io_name : String = DefaultIoName) : String\n # The iyi source that prints the template *string* to *io_name*, or nil\n # for a tag that is never closed.\n def self.process_string?(string : String, filename : String = \"\", io_name : String = DefaultIoName) : String | ::Nil\n # Defines `to_s(io)` and `to_s` from the template at *filename*. The\n # parameter is `__io__`, a name no template means, so a template's own\n # `io` stays its own.\n macro def_to_s(filename)\n # Splices the template at *filename* here; its code prints to *io_name*.\n macro embed(filename, io_name)\n # The template at *filename*, rendered to a `String`. The buffer's name is\n # the macro's own, so a template that renders another one inside it does\n # not replace the buffer it is printing to.\n macro render(filename)\nend\n" }, { @@ -202,7 +202,7 @@ { "name": "std/env", "path": "src/std/env.iyi", - "sourceSha256": "06aec191c04fd2d11460e913ad83152c3fe48262bd53bb23a7a8a91bda74b4dc", + "sourceSha256": "ab7ed2c470f59e8777eed4dcc54e22b8beee8689c672170655f20aa74a16c6f5", "surface": "module std/env\n\npub struct ENV\n def self.[](key : String) : String\n def self.[]=(key : String, value : String | ::Nil) : String | ::Nil\n def self.[]?(key : String) : String | ::Nil\n def self.clear : Nil\n def self.delete(key : String) : String | ::Nil\n def self.each(&block : (String, String -> Nil)) : Nil\n def self.each_key(&block : (String -> Nil)) : Nil\n def self.each_value(&block : (String -> Nil)) : Nil\n def self.empty? : Bool\n def self.fetch(key : String, default : T) : String | T forall T\n def self.fetch(key : String) : String\n def self.fetch(key : String, &block : (String -> T)) : String | T forall T\n def self.has_key?(key : String) : Bool\n def initialize\n def self.keys : Array(String)\n def self.size : Int32\n def self.to_h : Hash(String, String)\n def self.values : Array(String)\nend\n\nmodule ::ENV\n def self.[](key : String) : String\n def self.[]=(key : String, value : String | ::Nil) : String | ::Nil\n def self.[]?(key : String) : String | ::Nil\n def self.clear : Nil\n def self.delete(key : String) : String | ::Nil\n def self.each(&block : (String, String -> Nil)) : Nil\n def self.each_key(&block : (String -> Nil)) : Nil\n def self.each_value(&block : (String -> Nil)) : Nil\n def self.empty? : Bool\n def self.fetch(key : String, default : T) : String | T forall T\n def self.fetch(key : String) : String\n def self.fetch(key : String, &block : (String -> T)) : String | T forall T\n def self.has_key?(key : String) : Bool\n def self.keys : Array(String)\n def self.size : Int32\n def self.to_h : Hash(String, String)\n def self.values : Array(String)\nend\n" }, { @@ -214,13 +214,13 @@ { "name": "std/file", "path": "src/std/file.iyi", - "sourceSha256": "356dadb8c25c92194b4438cb0c8891fc4758042e8c3d8e7c25c58952e94643bb", + "sourceSha256": "ce84d306b22a03fe5e99e5e760d62370de61df273581e192fa1bd4a762a28450", "surface": "module std/file\n\npub alias File = ::File\n\n# Linux: the kernel's own calls, as `socket` and the prelude make them\n# (SPEC.md III.9: an own-prelude object asks libc for nothing). The\n# shape is libc's, so the callers above read the same; a failure comes\n# back as the negative errno the kernel answers, which every caller\n# here reads as \"not zero\". `realpath` is a walk over `readlinkat` and\n# `newfstatat`, since libc's is a library function and not a call.\npub struct LibC\n # x86_64 `struct stat`, 144 bytes, the layout glibc shares with the kernel.\n struct Stat\n def initialize\n def st_atime : Timespec\n def st_blksize : Int64\n def st_blocks : Int64\n def st_ctime : Timespec\n def st_dev : UInt64\n def st_gid : Int32\n def st_ino : UInt64\n def st_mode : Int32\n def st_mtime : Timespec\n def st_nlink : UInt64\n def st_pad0 : Int32\n def st_rdev : UInt64\n def st_reserved0 : Int64\n def st_reserved1 : Int64\n def st_reserved2 : Int64\n def st_size : Int64\n def st_uid : Int32\n end\n struct Timespec\n def initialize\n def tv_nsec : Int64\n def tv_sec : Int64\n end\n def self.access(path : ::Pointer(UInt8), mode : Int32) : Int32\n def self.chmod(path : ::Pointer(UInt8), mode : Int32) : Int32\n def self.chown(path : ::Pointer(UInt8), owner : Int32, group : Int32) : Int32\n def self.fstat(fd : Int32, buf : ::Pointer(Stat)) : Int32\n def self.ftruncate(fd : Int32, length : Int64) : Int32\n def initialize\n def self.link(oldpath : ::Pointer(UInt8), newpath : ::Pointer(UInt8)) : Int32\n def self.lstat(path : ::Pointer(UInt8), buf : ::Pointer(Stat)) : Int32\n def self.readlink(path : ::Pointer(UInt8), buf : ::Pointer(UInt8), bufsiz : UInt64) : Int64\n # The absolute path with every `.`, `..` and symbolic link resolved, or\n # null when a component does not exist. Forty links deep is the limit,\n # as it is in libc.\n def self.realpath(path : ::Pointer(UInt8), resolved : ::Pointer(UInt8)) : ::Pointer(UInt8)\n def self.rename(oldpath : ::Pointer(UInt8), newpath : ::Pointer(UInt8)) : Int32\n def self.stat(path : ::Pointer(UInt8), buf : ::Pointer(Stat)) : Int32\n def self.symlink(target : ::Pointer(UInt8), linkpath : ::Pointer(UInt8)) : Int32\n def self.sys(number : Int64, a : Int64, b : Int64, c : Int64, d : Int64 = 0_i64, e : Int64 = 0_i64) : Int64\n def self.truncate(path : ::Pointer(UInt8), length : Int64) : Int32\n # *sec* 0 is \"now\" (a null timespec, as utimensat documents). Otherwise\n # atime and mtime are both that Unix second.\n def self.utimensat(path : ::Pointer(UInt8), sec : Int64) : Int32\nend\n\nclass ::File\n def self.basename(path : String | Path, suffix : String | ::Nil = nil) : String\n def self.chmod(path : String | Path, mode : Int32) : Nil\n def self.chown(path : String | Path, uid : Int32, gid : Int32) : Nil\n def self.directory?(path : String | Path) : Bool\n def self.dirname(path : String | Path) : String\n # A line at a time, off the file as it is read: it read the whole file\n # into an array of its lines first, so a log of any size was the\n # program's memory twice over before the first line was seen. Lines as\n # `read_lines` gives them, without their `\\n` or a `\\r` before it.\n def self.each_line(path : String | Path, &block : (String -> Nil)) : Nil\n def self.empty?(path : String | Path) : Bool\n # On Windows a file runs by its extension, one of PATHEXT's - what cmd\n # and `CreateProcess`'s search go by - and a directory is searchable\n # when it can be read. `cmd.exe` answered false: the made-up mode gave\n # the execute bit to directories alone.\n def self.executable?(path : String | Path) : Bool\n def self.expand_path(path : String | Path, base : String | ::Nil = nil) : String\n def self.extname(path : String | Path) : String\n def self.file?(path : String | Path) : Bool\n def self.info(path : String | Path, follow_symlinks : Bool = true) : Info\n def self.info?(path : String | Path, follow_symlinks : Bool = true) : Info | ::Nil\n def self.join(first : String | Path, second : String | Path, third : String | Path) : String\n def self.join(first : String | Path, second : String | Path) : String\n def self.join(parts : Array(String)) : String\n def self.link(old_path : String | Path, new_path : String | Path) : Nil\n # Whether *path* matches the shell-style *pattern*: `*` any run of\n # characters within a segment, `**` any run of segments (a whole\n # segment of the pattern), `?` one character but a separator, `[abc]`,\n # `[a-z]` and `[^...]` one character of a set, `{a,b}` one of the\n # branches (nested to ten), and `\\` the next character literally. Only\n # `/` in the pattern matches a separator. On Windows a name's `\\` is a\n # separator as well, and a pattern's `\\` is one too rather than an\n # escape: `Dir.glob` answers with either.\n #\n # The other library's algorithm (a port of the glob-match crate, after\n # research.swtch.com/glob), linear in the name but for braces. This\n # had `*` and `?` alone, byte by byte: `*` crossed separators, `?` took\n # half of a two-byte character, `[a]` and `{a,b}` were literal text,\n # and `*a*a*a*a*a*a*a*a*a*a*b` against forty `a`s did not finish.\n def self.match?(path : String | Path, pattern : String) : Bool\n def self.read_lines(path : String | Path) : Array(String)\n def self.readable?(path : String | Path) : Bool\n def self.readlink(path : String | Path) : String\n def self.real_path(path : String | Path) : String\n def self.rename(old_path : String | Path, new_path : String | Path) : Nil\n def self.same?(path1 : String | Path, path2 : String | Path) : Bool\n def self.same_content?(path1 : String | Path, path2 : String | Path) : Bool\n def self.size(path : String | Path) : Int64\n def self.symlink(old_path : String | Path, new_path : String | Path) : Nil\n def self.symlink?(path : String | Path) : Bool\n # A new, empty file in the temporary directory, and its path.\n #\n # Made with an exclusive create - `O_CREAT | O_EXCL`, `CREATE_NEW` on\n # Windows - which fails when anything is at the path, a symlink\n # included, rather than asked whether something is there and written\n # after. The name was the process id and a counter, and the file was\n # written wherever the path led if nothing was there when asked: a\n # symlink planted at the next name in a shared `/tmp` between the\n # question and the write sent an upload wherever its owner pointed it.\n # The name is sixteen hex digits of the system's randomness now, so it\n # cannot be planted in advance either; Windows' `%TEMP%` belongs to the\n # user, and there the create's refusal is the guarantee and the name\n # needs only to be new. The file is the owner's alone, mode 0600.\n def self.tempfile(prefix : String = \"tmp\", suffix : String = \"\") : String\n # The file is deleted after the block if it is still there: the block\n # may have renamed it into place - write to a temporary name, then\n # rename, is what the name is for - and `delete` of a path that is gone\n # panicked after the block had done its work.\n def self.tempfile(prefix : String = \"tmp\", suffix : String = \"\", &block : (String -> Nil)) : Nil\n # *time* 0 is \"now\"; any other value is the Unix second both atime and\n # mtime are set to.\n #\n # The darwin branch passed a null `times` to `utimes`, which is the\n # kernel's spelling of \"now\" — so `touch(path, 1)` set the clock's time\n # there and the epoch second on Linux, one name with two behaviours\n # decided by the platform (SPEC.md III.1.7a). It writes the pair now, in\n # the same shape the Linux branch writes its `timespec`s: `timeval` is an\n # `Int64` second and an `Int32` microsecond padded to eight, so four\n # `Int64` slots are the two structures and the odd slots are the\n # microseconds, zero.\n def self.touch(path : String | Path, time : Int64 = 0_i64) : Nil\n def self.touch?(path : String | Path, time : Int64 = 0_i64) : Bool\n def self.truncate(path : String | Path, size : Int64 = 0_i64) : Nil\n def self.writable?(path : String | Path) : Bool\nend\n\nstruct ::File::Flags\n def initialize(value : Int32 = 0)\n def set_group? : Bool\n def set_user? : Bool\n def sticky? : Bool\n def value : Int32\nend\n\nstruct ::File::Info\n def dev : Int64\n def directory? : Bool\n def executable? : Bool\n def file? : Bool\n def flags : Flags\n def group_id : String\n def initialize(size : Int64, permissions : Permissions, type : Type, flags : Flags, modification_time : Int64, owner_id : String, group_id : String, dev : Int64 = 0_i64, ino : UInt64 = 0_u64)\n def ino : UInt64\n def modification_time : Int64\n def owner_id : String\n def permissions : Permissions\n def readable? : Bool\n # An identity of 0 is none: Windows answers a path it could not open\n # with the attributes alone (`info?`), and two such files - different\n # sizes, different contents - were the same file.\n def same_file?(other : Info) : Bool\n def size : Int64\n def symlink? : Bool\n def type : Type\n def writable? : Bool\nend\n\nstruct ::File::Permissions\n def group_execute? : Bool\n def group_read? : Bool\n def group_write? : Bool\n def initialize(value : Int32)\n def other_execute? : Bool\n def other_read? : Bool\n def other_write? : Bool\n def owner_execute? : Bool\n def owner_read? : Bool\n def owner_write? : Bool\n def to_i : Int32\n def to_s(io : IO) : Nil\n def to_s : String\n def value : Int32\nend\n\nenum ::File::Type : UInt8\n # Returns `true` if this enum value equals `BlockDevice`\n def block_device? : Bool\n # Returns `true` if this enum value equals `CharacterDevice`\n def character_device? : Bool\n def device? : Bool\n # Returns `true` if this enum value equals `Directory`\n def directory? : Bool\n # Returns `true` if this enum value equals `File`\n def file? : Bool\n # Returns `true` if this enum value equals `Pipe`\n def pipe? : Bool\n # Returns `true` if this enum value equals `Socket`\n def socket? : Bool\n # Returns `true` if this enum value equals `Symlink`\n def symlink? : Bool\nend\n\nclass ::IyiIO\n # Advisory locks on the whole file, the other library's `flock_*`: a\n # shared one many holders may have at once, an exclusive one a single\n # holder. The holder is this open file - another `File.open` of the same\n # path, in this process or another, is another holder - until\n # `flock_unlock`, or until the file is closed.\n #\n # File.open(\"app.lock\", \"w\") do |f|\n # f.flock_exclusive { run_alone }.or_panic\n # end\n #\n # A lock another holder has is waited for a task at a time: tried again\n # after 1, 2, 4 ... 64 ms, the thread's other tasks running meanwhile,\n # and cancellable. `try_flock_*` answers at once whether it took it.\n #\n # Linux and darwin lock with `flock(2)`, which only other locks heed, and\n # turn a shared lock this file holds into an exclusive one in place.\n # Windows locks every byte with `LockFileEx`, which is mandatory - while\n # one handle holds it exclusively, another handle's reads and writes of\n # the file fail - and counts each lock a handle takes: unlock a shared\n # lock before taking the exclusive one.\n def flock_exclusive : Nil | Cancelled\n # The block's answer, run with the lock held and unlocked after it, even\n # when the block panics.\n def flock_exclusive(&block : (-> R)) : R | Cancelled forall R\n def flock_shared : Nil | Cancelled\n def flock_shared(&block : (-> R)) : R | Cancelled forall R\n # Gives the lock back; one this file does not hold is no error.\n def flock_unlock : Nil\n # The kernel's move: the new offset, or a negative error. A stream with\n # no descriptor - a `Memory` - is -1, which Windows' `to_u64` of it\n # panicked \"arithmetic overflow\" on; it is EBADF everywhere, as Linux's\n # kernel answers it.\n protected def self.lseek(fd : Int64, offset : Int64, whence : Int32) : Int64\n # Bytes from the start to where the next read or write happens.\n def pos : Int64\n def pos=(position : Int64) : Int64\n # Moves to *offset* from the start, from here, or from the end, and\n # answers the new position.\n def seek(offset : Int64, whence : Seek = Seek::Set) : Int64\n def seek(offset : Int32, whence : Seek = Seek::Set) : Int64\n def try_flock_exclusive : Bool\n def try_flock_shared : Bool\nend\n\nenum ::IyiIO::Seek : Int32\nend\n" }, { "name": "std/file_utils", "path": "src/std/file_utils.iyi", - "sourceSha256": "85e44de643e944770f11657a7f258c5b9bbdd73b62835ae0a9d2bcd68c7f2483", + "sourceSha256": "09f8d63d2971dfd7c43443d9c5ea37c33baa5afb093b0239902e92a41a6193d0", "surface": "module std/file_utils\n\npub struct FileUtils\n # Whether the files *a* and *b* hold the same bytes, read a piece at a\n # time up to the first difference.\n def self.compare_file(a : String | Path, b : String | Path) : Bool\n # Copies the file *src* to *dst*, its bytes and its permission bits. A\n # *dst* that is a directory receives the file under its own name.\n def self.cp(src : String | Path, dst : String | Path) : Nil\n # Copies *src* - a file, a link or a whole tree - to *dst*, links as\n # links. A *dst* that is a directory receives *src* under its own name;\n # a directory already at the target is filled, as `cp -R` fills one.\n # `link/`, with its separator, is the directory the link names, as\n # `cp -R link/ x` copies that directory: a copy takes nothing away.\n def self.cp_r(src : String | Path, dst : String | Path) : Nil\n def initialize\n # Moves *src* to *dst*: a rename on one device, a copy and a removal\n # across two. A *dst* that is a directory receives *src* under its own\n # name. `link/` moves the link, as `rm_r` removes it: moving takes the\n # source away, and the directory a link names is not the caller's.\n def self.mv(src : String | Path, dst : String | Path) : Nil\n # Removes *path*: a file, a link (not what it names), or a directory\n # and everything under it. A path that is not there is refused.\n def self.rm_r(path : String | Path) : Nil\n # `rm_r`, with a path that is not there accepted.\n def self.rm_rf(path : String | Path) : Nil\nend\n" }, { @@ -232,20 +232,20 @@ { "name": "std/float", "path": "src/std/float.iyi", - "sourceSha256": "929e6a66b049e71e229207c040b400e488861e761c29ff168ba86a9375cf63c9", + "sourceSha256": "04e8ff026a7f499873173e3c83eb2c73c4dfd6cbdc39fc9fb6d546a19cfa9b3a", "surface": "module std/float\n\nstruct ::Float32\n def !=(other : Float64) : Bool\n def !=(other : Float32) : Bool\n def !=(other : Int8) : Bool\n def !=(other : Int16) : Bool\n def !=(other : Int32) : Bool\n def !=(other : Int64) : Bool\n def !=(other : Int128) : Bool\n def !=(other : UInt8) : Bool\n def !=(other : UInt16) : Bool\n def !=(other : UInt32) : Bool\n def !=(other : UInt64) : Bool\n def !=(other : UInt128) : Bool\n # The floored modulo, the divisor's sign: `-7.5 % 2.0` is 0.5,\n # `7.5 % -2.0` is -0.5, and a zero takes the divisor's sign too -\n # `-0.0 % 1.0` is `0.0` - where it used to keep the dividend's.\n def %(other : Float32) : Float32\n def *(other : Int8) : self\n def *(other : Int16) : self\n def *(other : Int32) : self\n def *(other : Int64) : self\n def *(other : Int128) : self\n def *(other : UInt8) : self\n def *(other : UInt16) : self\n def *(other : UInt32) : self\n def *(other : UInt64) : self\n def *(other : UInt128) : self\n def *(other : Float64) : self\n def *(other : Float32) : self\n def **(exponent : Int32) : Float32\n def +(other : Int8) : self\n def +(other : Int16) : self\n def +(other : Int32) : self\n def +(other : Int64) : self\n def +(other : Int128) : self\n def +(other : UInt8) : self\n def +(other : UInt16) : self\n def +(other : UInt32) : self\n def +(other : UInt64) : self\n def +(other : UInt128) : self\n def +(other : Float64) : self\n def +(other : Float32) : self\n def -(other : Int8) : self\n def -(other : Int16) : self\n def -(other : Int32) : self\n def -(other : Int64) : self\n def -(other : Int128) : self\n def -(other : UInt8) : self\n def -(other : UInt16) : self\n def -(other : UInt32) : self\n def -(other : UInt64) : self\n def -(other : UInt128) : self\n def -(other : Float64) : self\n def -(other : Float32) : self\n # Multiplied rather than subtracted from zero, so `-(0.0)` is `-0.0`.\n def - : Float32\n def /(other : Float32) : Float32\n # The floored quotient, `divmod`'s other half: `7.5 // 2.0` is 3.0\n # and `-7.5 // 2.0` is -4.0. The exact remainder is taken off first\n # so the division is of a multiple of the divisor; it can still round,\n # so the quotient is floored and nudged up past a half. A zero\n # quotient has the sign of the division.\n #\n # A single's is the double's, narrowed. Run in 24 bits, `self - r`\n # can round once the quotient is past 2^23, and the division then\n # floored to the next integer: `-170277296.0_f32 // -13.303335_f32`\n # was 12799595.0 for 12799594.0, and `divmod`'s q * y + r missed x by\n # 13.3.\n def //(other : Float32) : Float32\n def <(other : Float64) : Bool\n def <(other : Float32) : Bool\n def <(other : Int8) : Bool\n def <(other : Int16) : Bool\n def <(other : Int32) : Bool\n def <(other : Int64) : Bool\n def <(other : Int128) : Bool\n def <(other : UInt8) : Bool\n def <(other : UInt16) : Bool\n def <(other : UInt32) : Bool\n def <(other : UInt64) : Bool\n def <(other : UInt128) : Bool\n def <=(other : Float64) : Bool\n def <=(other : Float32) : Bool\n def <=(other : Int8) : Bool\n def <=(other : Int16) : Bool\n def <=(other : Int32) : Bool\n def <=(other : Int64) : Bool\n def <=(other : Int128) : Bool\n def <=(other : UInt8) : Bool\n def <=(other : UInt16) : Bool\n def <=(other : UInt32) : Bool\n def <=(other : UInt64) : Bool\n def <=(other : UInt128) : Bool\n # A total order, so `Comparable` holds and a list of floats sorts:\n # NaN is after everything and equal to NaN. `<` and `==` keep IEEE's\n # answer, false against NaN; this is the one place NaN is placed.\n def <=>(other : Float32) : Int32\n def ==(other : Float64) : Bool\n def ==(other : Float32) : Bool\n # Against an integer, exact: on the double the single is exactly,\n # which holds every integer under 64 bits exactly too, and through\n # std/int's exact `<=>` for the wider ones, which the prelude's\n # `Int64 == Float64` agrees with. `16777217 == 16777216.0_f32` was true,\n # the integer rounded to a single first.\n def ==(other : Int8) : Bool\n def ==(other : Int16) : Bool\n def ==(other : Int32) : Bool\n def ==(other : Int64) : Bool\n def ==(other : Int128) : Bool\n def ==(other : UInt8) : Bool\n def ==(other : UInt16) : Bool\n def ==(other : UInt32) : Bool\n def ==(other : UInt64) : Bool\n def ==(other : UInt128) : Bool\n def >(other : Float64) : Bool\n def >(other : Float32) : Bool\n def >(other : Int8) : Bool\n def >(other : Int16) : Bool\n def >(other : Int32) : Bool\n def >(other : Int64) : Bool\n def >(other : Int128) : Bool\n def >(other : UInt8) : Bool\n def >(other : UInt16) : Bool\n def >(other : UInt32) : Bool\n def >(other : UInt64) : Bool\n def >(other : UInt128) : Bool\n def >=(other : Float64) : Bool\n def >=(other : Float32) : Bool\n def >=(other : Int8) : Bool\n def >=(other : Int16) : Bool\n def >=(other : Int32) : Bool\n def >=(other : Int64) : Bool\n def >=(other : Int128) : Bool\n def >=(other : UInt8) : Bool\n def >=(other : UInt16) : Bool\n def >=(other : UInt32) : Bool\n def >=(other : UInt64) : Bool\n def >=(other : UInt128) : Bool\n # Every single is a double, and the integer nearest a single is a\n # single: the prelude's `Float64` roundings are exact for it.\n # The sign cleared, by `Float64#abs`: a single widens to a double and\n # back exactly, and a comparison left `(-0.0_f32).abs` at `-0.0`.\n def abs : Float32\n def ceil : Float32\n def fdiv(other : Int8) : self\n def fdiv(other : Int16) : self\n def fdiv(other : Int32) : self\n def fdiv(other : Int64) : self\n def fdiv(other : Int128) : self\n def fdiv(other : UInt8) : self\n def fdiv(other : UInt16) : self\n def fdiv(other : UInt32) : self\n def fdiv(other : UInt64) : self\n def fdiv(other : UInt128) : self\n def fdiv(other : Float64) : self\n def fdiv(other : Float32) : self\n def finite? : Bool\n def floor : Float32\n def hash : Int32\n # 1 for +Infinity, -1 for -Infinity, nil for anything finite or NaN.\n def infinite? : Int32 | ::Nil\n def nan? : Bool\n # The remainder of the truncated division, the dividend's sign (C's\n # `fmod`), computed exactly: the divisor is doubled up to the dividend\n # and subtracted, and every such difference is exact.\n def remainder(other : Float32) : Float32\n def round(digits : Int32) : Float32\n def round(mode : Std::Number::RoundingMode) : Float32\n def round : Float32\n def to_f : Float64\n def to_f32 : Float32\n def to_f64 : Float64\n def to_i : Int32\n def to_i128 : Int128\n def to_i16 : Int16\n def to_i32 : Int32\n def to_i64 : Int64\n # Truncated first, then refused if the truncation does not fit:\n # `255.5_f32.to_u8` is 255 and `(-0.5_f32).to_u8` is 0. The instruction\n # is std/int's, on the double every single is exactly.\n def to_i8 : Int8\n def to_s : String\n def to_u : UInt32\n def to_u128 : UInt128\n def to_u16 : UInt16\n def to_u32 : UInt32\n def to_u64 : UInt64\n def to_u8 : UInt8\n # Toward zero. Past 2^24 every value is an integer already and is\n # answered as it is; NaN and the infinities likewise. A zero keeps the\n # sign of what was truncated - `(-0.5).trunc` is -0.0, as `floor` and\n # `ceil` answer - where the conversion through an integer lost it.\n def trunc : Float32\nend\n\nstruct ::Float64\n def !=(other : Float32) : Bool\n # The floored modulo, the divisor's sign: `-7.5 % 2.0` is 0.5,\n # `7.5 % -2.0` is -0.5, and a zero takes the divisor's sign too -\n # `-0.0 % 1.0` is `0.0` - where it used to keep the dividend's.\n def %(other : Float64) : Float64\n def *(other : Float32) : self\n def +(other : Float32) : self\n def -(other : Float32) : self\n # Multiplied rather than subtracted from zero, so `-(0.0)` is `-0.0`.\n def - : Float64\n # The floored quotient, `divmod`'s other half: `7.5 // 2.0` is 3.0\n # and `-7.5 // 2.0` is -4.0. The exact remainder is taken off first\n # so the division is of a multiple of the divisor; it can still round,\n # so the quotient is floored and nudged up past a half. A zero\n # quotient has the sign of the division.\n #\n # A single's is the double's, narrowed. Run in 24 bits, `self - r`\n # can round once the quotient is past 2^23, and the division then\n # floored to the next integer: `-170277296.0_f32 // -13.303335_f32`\n # was 12799595.0 for 12799594.0, and `divmod`'s q * y + r missed x by\n # 13.3.\n def //(other : Float64) : Float64\n def <(other : Float32) : Bool\n def <=(other : Float32) : Bool\n # A total order, so `Comparable` holds and a list of floats sorts:\n # NaN is after everything and equal to NaN. `<` and `==` keep IEEE's\n # answer, false against NaN; this is the one place NaN is placed.\n def <=>(other : Float64) : Int32\n def ==(other : Float32) : Bool\n def >(other : Float32) : Bool\n def >=(other : Float32) : Bool\n def fdiv(other : Float32) : self\n def finite? : Bool\n # 1 for +Infinity, -1 for -Infinity, nil for anything finite or NaN.\n def infinite? : Int32 | ::Nil\n def nan? : Bool\n # The remainder of the truncated division, the dividend's sign (C's\n # `fmod`), computed exactly: the divisor is doubled up to the dividend\n # and subtracted, and every such difference is exact.\n def remainder(other : Float64) : Float64\n # The instruction, unchecked: a double past the single's range\n # narrows to the infinity of its sign, as `Float32::MAX * 2.0_f32`\n # answers. It used to panic \"arithmetic overflow\" here alone.\n def to_f32 : Float32\n # Toward zero. Past 2^53 every value is an integer already and is\n # answered as it is; NaN and the infinities likewise. A zero keeps the\n # sign of what was truncated - `(-0.5).trunc` is -0.0, as `floor` and\n # `ceil` answer - where the conversion through an integer lost it.\n def trunc : Float64\nend\n\nstruct ::Int128\n def !=(other : Float32) : Bool\n def *(other : Float32) : Float32\n def +(other : Float32) : Float32\n def -(other : Float32) : Float32\n def /(other : Float32) : Float32\n # An integer over an integer is a double, as the other library\n # divides one, and written out so that an integer divisor matches\n # without a cast: beside `/(Float64)` and the `/(Float32)` above,\n # the literal in `7 / 2` cast to both, and importing this module\n # made that line \"ambiguous call\" where it had printed 3.5.\n def /(other : ::Int) : Float64\n def <(other : Float32) : Bool\n def <=(other : Float32) : Bool\n # The single's exact comparison, with the sides swapped.\n def ==(other : Float32) : Bool\n def >(other : Float32) : Bool\n def >=(other : Float32) : Bool\n def to_f32 : Float32\nend\n\nstruct ::Int16\n def !=(other : Float32) : Bool\n def *(other : Float32) : Float32\n def +(other : Float32) : Float32\n def -(other : Float32) : Float32\n def /(other : Float32) : Float32\n # An integer over an integer is a double, as the other library\n # divides one, and written out so that an integer divisor matches\n # without a cast: beside `/(Float64)` and the `/(Float32)` above,\n # the literal in `7 / 2` cast to both, and importing this module\n # made that line \"ambiguous call\" where it had printed 3.5.\n def /(other : ::Int) : Float64\n def <(other : Float32) : Bool\n def <=(other : Float32) : Bool\n # The single's exact comparison, with the sides swapped.\n def ==(other : Float32) : Bool\n def >(other : Float32) : Bool\n def >=(other : Float32) : Bool\n def to_f32 : Float32\nend\n\nstruct ::Int32\n def !=(other : Float32) : Bool\n def *(other : Float32) : Float32\n def +(other : Float32) : Float32\n def -(other : Float32) : Float32\n def /(other : Float32) : Float32\n # An integer over an integer is a double, as the other library\n # divides one, and written out so that an integer divisor matches\n # without a cast: beside `/(Float64)` and the `/(Float32)` above,\n # the literal in `7 / 2` cast to both, and importing this module\n # made that line \"ambiguous call\" where it had printed 3.5.\n def /(other : ::Int) : Float64\n def <(other : Float32) : Bool\n def <=(other : Float32) : Bool\n # The single's exact comparison, with the sides swapped.\n def ==(other : Float32) : Bool\n def >(other : Float32) : Bool\n def >=(other : Float32) : Bool\n def to_f32 : Float32\nend\n\nstruct ::Int64\n def !=(other : Float32) : Bool\n def *(other : Float32) : Float32\n def +(other : Float32) : Float32\n def -(other : Float32) : Float32\n def /(other : Float32) : Float32\n # An integer over an integer is a double, as the other library\n # divides one, and written out so that an integer divisor matches\n # without a cast: beside `/(Float64)` and the `/(Float32)` above,\n # the literal in `7 / 2` cast to both, and importing this module\n # made that line \"ambiguous call\" where it had printed 3.5.\n def /(other : ::Int) : Float64\n def <(other : Float32) : Bool\n def <=(other : Float32) : Bool\n # The single's exact comparison, with the sides swapped.\n def ==(other : Float32) : Bool\n def >(other : Float32) : Bool\n def >=(other : Float32) : Bool\n def to_f32 : Float32\nend\n\nstruct ::Int8\n def !=(other : Float32) : Bool\n def *(other : Float32) : Float32\n def +(other : Float32) : Float32\n def -(other : Float32) : Float32\n def /(other : Float32) : Float32\n # An integer over an integer is a double, as the other library\n # divides one, and written out so that an integer divisor matches\n # without a cast: beside `/(Float64)` and the `/(Float32)` above,\n # the literal in `7 / 2` cast to both, and importing this module\n # made that line \"ambiguous call\" where it had printed 3.5.\n def /(other : ::Int) : Float64\n def <(other : Float32) : Bool\n def <=(other : Float32) : Bool\n # The single's exact comparison, with the sides swapped.\n def ==(other : Float32) : Bool\n def >(other : Float32) : Bool\n def >=(other : Float32) : Bool\n def to_f32 : Float32\nend\n\nstruct ::UInt128\n def !=(other : Float32) : Bool\n def *(other : Float32) : Float32\n def +(other : Float32) : Float32\n def -(other : Float32) : Float32\n def /(other : Float32) : Float32\n # An integer over an integer is a double, as the other library\n # divides one, and written out so that an integer divisor matches\n # without a cast: beside `/(Float64)` and the `/(Float32)` above,\n # the literal in `7 / 2` cast to both, and importing this module\n # made that line \"ambiguous call\" where it had printed 3.5.\n def /(other : ::Int) : Float64\n def <(other : Float32) : Bool\n def <=(other : Float32) : Bool\n # The single's exact comparison, with the sides swapped.\n def ==(other : Float32) : Bool\n def >(other : Float32) : Bool\n def >=(other : Float32) : Bool\n def to_f32 : Float32\nend\n\nstruct ::UInt16\n def !=(other : Float32) : Bool\n def *(other : Float32) : Float32\n def +(other : Float32) : Float32\n def -(other : Float32) : Float32\n def /(other : Float32) : Float32\n # An integer over an integer is a double, as the other library\n # divides one, and written out so that an integer divisor matches\n # without a cast: beside `/(Float64)` and the `/(Float32)` above,\n # the literal in `7 / 2` cast to both, and importing this module\n # made that line \"ambiguous call\" where it had printed 3.5.\n def /(other : ::Int) : Float64\n def <(other : Float32) : Bool\n def <=(other : Float32) : Bool\n # The single's exact comparison, with the sides swapped.\n def ==(other : Float32) : Bool\n def >(other : Float32) : Bool\n def >=(other : Float32) : Bool\n def to_f32 : Float32\nend\n\nstruct ::UInt32\n def !=(other : Float32) : Bool\n def *(other : Float32) : Float32\n def +(other : Float32) : Float32\n def -(other : Float32) : Float32\n def /(other : Float32) : Float32\n # An integer over an integer is a double, as the other library\n # divides one, and written out so that an integer divisor matches\n # without a cast: beside `/(Float64)` and the `/(Float32)` above,\n # the literal in `7 / 2` cast to both, and importing this module\n # made that line \"ambiguous call\" where it had printed 3.5.\n def /(other : ::Int) : Float64\n def <(other : Float32) : Bool\n def <=(other : Float32) : Bool\n # The single's exact comparison, with the sides swapped.\n def ==(other : Float32) : Bool\n def >(other : Float32) : Bool\n def >=(other : Float32) : Bool\n def to_f32 : Float32\nend\n\nstruct ::UInt64\n def !=(other : Float32) : Bool\n def *(other : Float32) : Float32\n def +(other : Float32) : Float32\n def -(other : Float32) : Float32\n def /(other : Float32) : Float32\n # An integer over an integer is a double, as the other library\n # divides one, and written out so that an integer divisor matches\n # without a cast: beside `/(Float64)` and the `/(Float32)` above,\n # the literal in `7 / 2` cast to both, and importing this module\n # made that line \"ambiguous call\" where it had printed 3.5.\n def /(other : ::Int) : Float64\n def <(other : Float32) : Bool\n def <=(other : Float32) : Bool\n # The single's exact comparison, with the sides swapped.\n def ==(other : Float32) : Bool\n def >(other : Float32) : Bool\n def >=(other : Float32) : Bool\n def to_f32 : Float32\nend\n\nstruct ::UInt8\n def !=(other : Float32) : Bool\n def *(other : Float32) : Float32\n def +(other : Float32) : Float32\n def -(other : Float32) : Float32\n def /(other : Float32) : Float32\n # An integer over an integer is a double, as the other library\n # divides one, and written out so that an integer divisor matches\n # without a cast: beside `/(Float64)` and the `/(Float32)` above,\n # the literal in `7 / 2` cast to both, and importing this module\n # made that line \"ambiguous call\" where it had printed 3.5.\n def /(other : ::Int) : Float64\n def <(other : Float32) : Bool\n def <=(other : Float32) : Bool\n # The single's exact comparison, with the sides swapped.\n def ==(other : Float32) : Bool\n def >(other : Float32) : Bool\n def >=(other : Float32) : Bool\n def to_f32 : Float32\nend\n" }, { "name": "std/format", "path": "src/std/format.iyi", - "sourceSha256": "2240322c8d1e77427067c30c5316ef8a8b4911ea4841ec3d9fd5e459b6f579d1", + "sourceSha256": "6ca6771f01c883764e39cad55977226793520c4f63e3a955ad3c4dd77d5f5b98", "surface": "module std/format\n\n# The argument list as one array or tuple, as `sprintf` takes it.\npub def printf(format_string : String, args : Array) : Nil\n\npub def printf(format_string : String, args : Tuple) : Nil\n\npub def printf(format_string : String, *args : *T) : Nil forall T\n\n# The argument list may be one array or tuple, as the other library takes\n# it: `\"%s, %s, %s, D\" % ['A', 'B', 'C']`, its own example, panicked \"too\n# few arguments for format string\" here, and `\"[%s]\" % [\"A\"]` printed\n# `[[\"A\"]]` where it prints `[A]`. Two overloads rather than one taking\n# `Array | Tuple`: a union with Array in it cannot be written into an\n# artifact (\"can't use Array(T) in unions yet\").\npub def sprintf(format_string : String, args : Array) : String\n\npub def sprintf(format_string : String, args : Tuple) : String\n\npub def sprintf(format_string : String, *args : *T) : String forall T\n\nclass ::IyiBig\n # self / small for a `small` under 2^32, self reduced to the quotient,\n # the remainder answered. A limb is 32 bits in a 64-bit word, so the\n # running remainder shifted up by a limb still fits the word.\n def div_small(small : UInt64) : UInt64\n # Base ten, most significant digit first, nine digits per division.\n def to_decimal : String\nend\n\nmodule ::IyiFloatText\n # Extracts Dragon4 digits for a non-zero, finite double.\n # Returns {digits_pointer, count, k} where value = 0.d1 d2 ... dn * 10^k.\n def self.dragon4(value : Float64) : ::Tuple(Pointer(UInt8), Int32, Int32)\n # Scientific notation with exact places, half to even.\n def self.format_exp(value : Float64, uppercase : Bool, precision : Int32, hash_flag : Bool) : String\n # Fixed-point decimal format with exact places, half to even.\n def self.format_fixed(value : Float64, precision : Int32) : String\n # a * b as its high and low words, from 32-bit halves.\n def self.mul_wide(a : UInt64, b : UInt64) : ::Tuple(UInt64, UInt64)\n # Every digit the double has, not the shortest ones padded with zeros:\n # `round(f * 2^e * 10^places)`, half to even, as decimal digits. This is\n # what a fixed or scientific format prints, and what it did not print:\n # `%.0f` of 1e23 answered 100000000000000000000000 where the double is\n # 99999999999999991611392, `%.20f` of 0.1 answered twenty zeros where\n # the value's digits are 0.10000000000000000555, and `%e` of the\n # smallest denormal answered 5.000000e-324 for 4.940656e-324 - the\n # shortest digits, which round-trip, are not the value, and past them\n # there was nothing but zeros. `places` may be negative, a scale down.\n def self.scaled_digits(f : UInt64, e : Int32, places : Int32) : String\n # `scaled_digits` in two words, when they hold it: f * 10^places is\n # under 2^117 for the places a word of ten's powers reaches, and the\n # quotient by 2^-e and its half-to-even rounding are shifts and one\n # comparison of the remainder with the half. The same integer the\n # bignum's division gives - it is the same arithmetic, exact - where\n # that division made `%.2f` three times what Go's takes. nil when the\n # value, the places or the answer do not fit.\n def self.scaled_quick(f : UInt64, e : Int32, places : Int32) : UInt64 | ::Nil\nend\n\nmodule ::IyiFormat\n # Any integer: an `Int64` or a `UInt64`, whose top half `to_i64` refuses.\n def self.code_point(code) : Char\n # A width or a precision, which an `Int32` holds: read into one, the\n # digits of `%99999999999d` panicked \"arithmetic overflow\".\n def self.field_number(format_string : String, start : Int32, stop : Int32, what : String) : Int32\n def self.format_arg(val, verb : Char, minus : Bool, plus : Bool, space : Bool, zero : Bool, hash : Bool, width : Int32, precision : Int32) : String\n # A number is a code point, as Crystal's `%c` has it: `%c` of 233 is\n # `é`, where it panicked \"233 is out of char range\" (`chr` is ASCII).\n # Anything else is its first character, not its first byte, which cut\n # `é` in half.\n def self.format_char_arg(val, minus : Bool, width : Int32) : String\n # Digits as `to_s(base)` writes them, a `-` first when negative, given\n # the verb's sign, prefix, precision and width.\n def self.format_digits(text : String, verb : Char, base : Int32, minus : Bool, plus : Bool, space : Bool, zero : Bool, hash : Bool, width : Int32, precision : Int32) : String\n def self.format_float64(val : Float64, minus : Bool, plus : Bool, space : Bool, zero : Bool, hash : Bool, width : Int32, precision : Int32) : String\n def self.format_float_exp(val : Float64, uppercase : Bool, minus : Bool, plus : Bool, space : Bool, zero : Bool, hash : Bool, width : Int32, precision : Int32) : String\n # `%g`: the shorter of the two notations, C's rule. With P the precision\n # (6 when none is given, 1 when it is 0) and X the exponent `%e` would\n # write at P - 1 places, it is `%e` at P - 1 places when X < -4 or X >= P\n # and `%f` at P - 1 - X places otherwise; either way the trailing zeros\n # of the fraction go, and its point with them, unless `#` keeps them.\n # NaN and the infinities are written as `%e` writes them.\n def self.format_float_general(val : Float64, uppercase : Bool, minus : Bool, plus : Bool, space : Bool, zero : Bool, hash : Bool, width : Int32, precision : Int32) : String\n def self.format_int64(val : Int64, base : Int32, uppercase : Bool, signed : Bool, minus : Bool, plus : Bool, space : Bool, zero : Bool, hash : Bool, width : Int32, precision : Int32) : String\n # Width and precision in characters, because `size` is and because\n # `%-10s` is a column: counted in bytes, a multi-byte string came out\n # short by one space per extra byte, and a precision cut a character in\n # half. The prelude's `ljust`/`rjust` decided the same thing.\n #\n # The characters a precision keeps are written into one buffer, as\n # `each_char` decodes them and `size` counts them: added one at a time\n # onto a string of their own, the text was copied once per character,\n # and `%.800000s` of a megabyte took 12.8 s, four times as long for\n # every doubling.\n def self.format_string_arg(str : String, minus : Bool, width : Int32, precision : Int32) : String\n # A float under an integer verb is truncated, which is what Ruby, Python\n # and the other language answer to it. Past an `Int64` the truncation is\n # the double's own integer, f * 2^e, written whole as Python writes it:\n # `%d` of 1e30 is 1000000000000000019884624838656, where `to_i64`\n # panicked \"arithmetic overflow\". NaN and the infinities have no\n # integer, and the refusal says that rather than \"arithmetic overflow\".\n def self.format_truncated(val : Float64, verb : Char, minus : Bool, plus : Bool, space : Bool, zero : Bool, hash : Bool, width : Int32, precision : Int32) : String\n def self.format_uint64(val : UInt64, base : Int32, uppercase : Bool, signed : Bool, minus : Bool, plus : Bool, space : Bool, zero : Bool, hash : Bool, width : Int32, precision : Int32) : String\n # An `Int128` or `UInt128`: an integer verb writes its digits, which may\n # not fit 64 bits; a float verb its nearest double, as `%f` of an\n # `Int64` is.\n def self.format_wide(val, verb : Char, minus : Bool, plus : Bool, space : Bool, zero : Bool, hash : Bool, width : Int32, precision : Int32) : String\n # A numeric verb is a claim about the argument, and an argument that is\n # not a number contradicts it: `%d` of \"abc\" printed abc, `%f` of a\n # `Char` printed the character, and the caller who had mistyped the\n # argument order saw their strings where their numbers should be and\n # nothing said so.\n def self.not_a_number(val, verb : Char) : NoReturn\n # A decimal's fraction without its trailing zeros, and without its point\n # when nothing is left after it: `%g`'s trim.\n def self.trim_fraction(text : String) : String\nend\n\nclass ::String\n def %(args : Tuple) : String\n def %(arg) : String\n def %(*args : *T) : String forall T\n def self.format(format_string : String, *args : *T) : String forall T\nend\n" }, { "name": "std/gc", "path": "src/std/gc.iyi", - "sourceSha256": "dc56a7014fc7262d00e80c6bc39bc2b1e613f9476aa2f370df3bebb890529fc2", - "surface": "module std/gc\n\npub struct GC\n # What the collector can answer, read at one instant.\n struct Stats\n # Bytes the last sweep handed back, and bytes it found still live.\n def bytes_freed_by_last_sweep : UInt64\n def bytes_kept_by_last_sweep : UInt64\n # Bytes allocated since the last collection: the word the allocation\n # trigger reads, folded in per cache slice and reset by every\n # collection.\n def bytes_since_collection : UInt64\n # How many collections have run.\n def collections : UInt64\n def initialize(mapped_bytes : UInt64, bytes_since_collection : UInt64, bytes_freed_by_last_sweep : UInt64, bytes_kept_by_last_sweep : UInt64, collections : UInt64)\n # Bytes the arenas and large mappings hold from the machine. A\n # reservation, counted in whole mappings; not bytes in use.\n def mapped_bytes : UInt64\n def to_s : String\n end\n # Runs one collection now.\n def self.collect : Nil\n # The allocation trigger stops running collections until `enable`.\n def self.disable : Nil\n # The allocation trigger runs collections again. `collect` still works\n # while disabled.\n def self.enable : Nil\n # Hands a chunk back before the sweep would. A null pointer is nothing\n # to free.\n def self.free(pointer : ::Pointer(Void)) : Nil\n def initialize\n # Whether `pointer` points into a live chunk on the collector's heap:\n # its start or any byte inside it. Null, a stack address and a freed\n # chunk answer false.\n #\n # Under the runtime lock, as `stats` is: a word in no arena walks the\n # large-mapping list, and another thread's `free` or sweep unlinks and\n # unmaps those nodes under that lock. Walked without it, beside three\n # threads whose large chunks come and go, two seconds of `is_heap_ptr`\n # died of a memory fault in 10 runs of 10 (`bench/std_gc_exercise.iyi`).\n def self.is_heap_ptr(pointer : ::Pointer(Void)) : Bool\n # `size` zeroed bytes the collector scans for pointers. A negative size\n # is refused by name.\n def self.malloc(size : Int) : ::Pointer(Void)\n # `size` bytes the collector does not scan: for bytes that hold no\n # pointers, so the marker skips them. They are not cleared - a chunk the\n # sweep handed back comes out with its last owner's bytes, as Boehm's\n # `GC_malloc_atomic` hands them out - so the caller writes every byte\n # it reads. This said \"zeroed\" while 2000 chunks taken after a\n # collection freed 2000 filled ones came back dirty, every one.\n def self.malloc_atomic(size : Int) : ::Pointer(Void)\n # The chunk at `pointer`, resized to `size` bytes; the answer may be a\n # new address, with the old bytes copied.\n def self.realloc(pointer : ::Pointer(Void), size : Int) : ::Pointer(Void)\n # The numbers the collector keeps, read now.\n #\n # The walk holds the runtime lock, which a collection takes before it\n # stops anything and holds while the scavenge unlinks an arena or a\n # large mapping. Walked without it, a collection another thread's\n # allocation started could stop this walk on a mapping, unlink and unmap\n # it, and the walk's next read was of memory gone: beside three threads\n # allocating, two seconds of `stats` died of a memory fault in 10 runs\n # of 10 (`bench/std_gc_exercise.iyi`).\n def self.stats : Stats\nend\n" + "sourceSha256": "86f9dd461f500a4ee6ec3b4f60a6c2216ce762ed9bc1b8a01a3363e5f12b3f07", + "surface": "module std/gc\n\npub struct GC\n # What the collector can answer, read at one instant.\n struct Stats\n # Bytes the last sweep handed back, and bytes it found still live.\n def bytes_freed_by_last_sweep : UInt64\n def bytes_kept_by_last_sweep : UInt64\n # Bytes allocated since the last collection: the word the allocation\n # trigger reads, folded in per cache slice and reset by every\n # collection.\n def bytes_since_collection : UInt64\n # How many collections have run.\n def collections : UInt64\n def initialize(mapped_bytes : UInt64, bytes_since_collection : UInt64, bytes_freed_by_last_sweep : UInt64, bytes_kept_by_last_sweep : UInt64, collections : UInt64)\n # Bytes the arenas and large mappings hold from the machine. A\n # reservation, counted in whole mappings; not bytes in use.\n def mapped_bytes : UInt64\n def to_s : String\n end\n # Runs one collection now. Always inlined, as `IyiMark.collect` is, so\n # that the dead stack is cleared from the caller's frame. Out of line\n # this is a frame of its own above the cleared stretch, 40 bytes nothing\n # writes (its call's home space and an alignment word), and a dead\n # helper's address filling the stack under the caller was freed in 20\n # --release runs of 20 only because the collection's next frame pushes\n # five registers over exactly those 40 bytes (LLVM 20.1.7; Windows 11,\n # 12 logical processors, the user's machine).\n def self.collect : Nil\n # The allocation trigger stops running collections until `enable`.\n def self.disable : Nil\n # The allocation trigger runs collections again. `collect` still works\n # while disabled.\n def self.enable : Nil\n # Hands a chunk back before the sweep would. A null pointer is nothing\n # to free.\n def self.free(pointer : ::Pointer(Void)) : Nil\n def initialize\n # Whether `pointer` points into a live chunk on the collector's heap:\n # its start or any byte inside it. Null, a stack address and a freed\n # chunk answer false.\n #\n # Under the runtime lock, as `stats` is: a word in no arena walks the\n # large-mapping list, and another thread's `free` or sweep unlinks and\n # unmaps those nodes under that lock. Walked without it, beside three\n # threads whose large chunks come and go, two seconds of `is_heap_ptr`\n # died of a memory fault in 10 runs of 10 (`bench/std_gc_exercise.iyi`).\n def self.is_heap_ptr(pointer : ::Pointer(Void)) : Bool\n # `size` zeroed bytes the collector scans for pointers. A negative size\n # is refused by name.\n def self.malloc(size : Int) : ::Pointer(Void)\n # `size` bytes the collector does not scan: for bytes that hold no\n # pointers, so the marker skips them. They are not cleared - a chunk the\n # sweep handed back comes out with its last owner's bytes, as Boehm's\n # `GC_malloc_atomic` hands them out - so the caller writes every byte\n # it reads. This said \"zeroed\" while 2000 chunks taken after a\n # collection freed 2000 filled ones came back dirty, every one.\n def self.malloc_atomic(size : Int) : ::Pointer(Void)\n # The chunk at `pointer`, resized to `size` bytes; the answer may be a\n # new address, with the old bytes copied.\n def self.realloc(pointer : ::Pointer(Void), size : Int) : ::Pointer(Void)\n # The numbers the collector keeps, read now.\n #\n # The walk holds the runtime lock, which a collection takes before it\n # stops anything and holds while the scavenge unlinks an arena or a\n # large mapping. Walked without it, a collection another thread's\n # allocation started could stop this walk on a mapping, unlink and unmap\n # it, and the walk's next read was of memory gone: beside three threads\n # allocating, two seconds of `stats` died of a memory fault in 10 runs\n # of 10 (`bench/std_gc_exercise.iyi`).\n def self.stats : Stats\nend\n" }, { "name": "std/heap", @@ -256,26 +256,26 @@ { "name": "std/hpack", "path": "src/std/hpack.iyi", - "sourceSha256": "b360a4741701255597bd44285eab9b6577e63afd86cdc4425f8008b21a3a781c", + "sourceSha256": "c72de4348698bd74543373c703ca968a204b66bd169e61bc982273ff0f5492c9", "surface": "module std/hpack\n\npub alias Bytes = Std::Slice::Bytes\n\npub class Decoder\n def decode(payload : Bytes) : Array(HeaderField) | HpackError\n def dynamic_table : DynamicTable\n def initialize(max_table_size : Int32 = 4096, protocol_max_table_size : Int32 = 4096, max_header_list_size : Int32 = 65536, max_header_count : Int32 = 1000)\n def max_header_count : Int32\n def max_header_count=(value : Int32) : Int32\n def max_header_list_size : Int32\n def max_header_list_size=(value : Int32) : Int32\n def protocol_max_table_size : Int32\n # A SETTINGS_HEADER_TABLE_SIZE change reaches the table too: the limit\n # was only this field's, and the table went on refusing an update above\n # the size it was made with.\n def protocol_max_table_size=(limit : Int32) : Nil\nend\n\npub class DynamicTable\n def add(name : String, value : String, never_indexed : Bool = false) : Nil\n def add(field : HeaderField) : Nil\n def empty? : Bool\n def entries : Array(HeaderField)\n def find(name : String, value : String) : Tuple(Int32, Bool)\n def find_name(name : String) : Int32\n def get(relative_index : Int32) : HeaderField | ::Nil\n # A table is made within the limit it is resized within: made above it,\n # an encoder's table outgrew the peer's, and an Encoder made with 8192\n # sent blocks a default Decoder refused (\"Index 121 out of range (max:\n # 103)\") while `set_max_size(8192)` on it was refused.\n def initialize(max_size : Int32 = 4096, settings_max_size : Int32 = 4096)\n def length : Int32\n def max_size : Int32\n def set_max_size(new_max : Int32) : Nil | HpackError\n def settings_max_size : Int32\n # The protocol's limit (SETTINGS_HEADER_TABLE_SIZE) as it changes during\n # a connection; the size in use still moves only by `set_max_size`.\n def settings_max_size=(limit : Int32) : Nil\n def size : Int32\n def self.size_error(new_max : Int32, limit : Int32) : HpackError | ::Nil\nend\n\npub class Encoder\n def dynamic_table : DynamicTable\n def encode(headers : Array(HeaderField), force_huffman : Bool | ::Nil = nil) : Bytes\n def initialize(max_table_size : Int32 = 4096, protocol_max_table_size : Int32 = 4096, use_huffman : Bool = true)\n def protocol_max_table_size : Int32\n # A SETTINGS_HEADER_TABLE_SIZE change reaches the table too: the limit\n # was only this field's, so a raised one was refused by the table and a\n # lowered one left the table above it (RFC 7541 4.2), with no update.\n # A lowered limit is always signalled: the peer's table may still be the\n # size the old limit let it start at, which this encoder never sent.\n def protocol_max_table_size=(limit : Int32) : Nil\n def set_max_table_size(new_max : Int32) : Nil | HpackError\n def use_huffman : Bool\n def use_huffman=(value : Bool) : Bool\nend\n\npub struct HeaderField\n def ==(other : HeaderField) : Bool\n def indexing : IndexingMode\n def initialize(name : String, value : String, indexing : IndexingMode = IndexingMode::Incremental)\n def initialize(name : String, value : String, never_indexed : Bool)\n def name : String\n def never_indexed : Bool\n def size : Int32\n def to_s : String\n def value : String\nend\n\npub struct HpackError\n def initialize(message : String)\n def to_s : String\nend\n\npub class Huffman\n def self.decode(bytes : Bytes, offset : Int32, length : Int32) : String | HpackError\n def self.decode(bytes : Bytes) : String | HpackError\n def self.encode(input : String, buffer : Array(UInt8)) : Nil\n def self.encode(input : String) : Bytes\n def self.encode(input : Bytes) : Bytes\n def self.encoded_size(input : String) : Int32\nend\n\npub struct IndexingMode\n def ==(other : IndexingMode) : Bool\n def initialize(value : Int32)\n def value : Int32\nend\n\npub class Integer\n def self.decode(bytes : Bytes, offset : Int32, prefix_bits : Int32) : Tuple(UInt64, Int32) | HpackError\n def self.encode(val : UInt64, prefix_bits : Int32, prefix_byte : UInt8, buffer : Array(UInt8)) : Nil\n def self.encode(val : UInt64, prefix_bits : Int32, prefix_byte : UInt8 = 0_u8) : Bytes\nend\n\npub alias IntegerCodec = Integer\n\npub class StaticTable\n def self.find(name : String, value : String) : Tuple(Int32, Bool)\n def self.find_name(name : String) : Int32\n def self.get(index : Int32) : HeaderField | ::Nil\n def self.size : Int32\nend\n\npub class Table\n def self.find(dynamic_table : DynamicTable, name : String, value : String) : Tuple(Int32, Bool)\n def self.find_name(dynamic_table : DynamicTable, name : String) : Int32\n def self.get(dynamic_table : DynamicTable, global_index : Int32) : HeaderField | ::Nil\nend\n\nimpl Error for HpackError\n def message : String\nend\n" }, { "name": "std/hpke", "path": "src/std/hpke.iyi", - "sourceSha256": "5435d94d48664066af94dd296f2ca5dad22025223f78530a40001a6863f66ccc", + "sourceSha256": "51924376d3ea7ea37f3e3ac541cd1066e3af7119f0e4da417cb573ee29794f8b", "surface": "module std/hpke\n\n# The AEADs, by their code points (section 7.3, table 5).\npub enum HpkeAead : Int32\n Aes128Gcm = 1\n Aes256Gcm = 2\n ChaCha20Poly1305 = 3\n ExportOnly = 65535\n # Nk: the bytes of a key; none for export-only.\n def key_size : Int32\n def name : String\n # Nn: the bytes of a nonce; none for export-only.\n def nonce_size : Int32\n # Nt: the bytes of a tag; none for export-only.\n def tag_size : Int32\nend\n\n# What is wrong with an encapsulation, a key, a ciphertext or a suite from\n# outside, or a context out of nonces: `message` is the sentence.\npub struct HpkeError\n def initialize(message : String)\n def to_s : String\nend\n\n# The KDFs, by their code points (section 7.2, table 3).\npub enum HpkeKdf : Int32\n HkdfSha256 = 1\n HkdfSha384 = 2\n HkdfSha512 = 3\n # HKDF-Expand (RFC 5869 section 2.3) of *prk* to *length* bytes.\n def expand(prk : Array(UInt8), info : Array(UInt8), length : Int32) : Array(UInt8)\n # Nh: the bytes of the hash's output.\n def hash_size : Int32\n # HMAC under the hash, keyed with *key*.\n def mac(key : String | Array(UInt8)) : HMAC\n def name : String\nend\n\n# The KEMs, by their code points (section 7.1, table 2).\npub enum HpkeKem : Int32\n P256Sha256 = 16\n P384Sha384 = 17\n X25519Sha256 = 32\n # Decap, or AuthDecap when *sender_public* is given (section 4.1): the\n # shared secret *enc* carries to *key*, or an `HpkeError` for an *enc*\n # of the wrong length, not a point of the curve, or of small order.\n def decapsulate(key : HpkePrivateKey, enc : String | Array(UInt8), sender_public : HpkePublicKey | Nil = nil) : Array(UInt8) | HpkeError\n # Encap, or AuthEncap when *sender_key* is given (section 4.1): the\n # encapsulation, enc, and the shared secret, from the ephemeral key\n # DeriveKeyPair makes of *randomness* (at least Nsk bytes). An\n # `HpkeError` when a Diffie-Hellman step is refused: *recipient* is an\n # X25519 key of small order.\n def encapsulate(recipient : HpkePublicKey, randomness : String | Array(UInt8), sender_key : HpkePrivateKey | Nil = nil) : ::Tuple(Array(UInt8), Array(UInt8)) | HpkeError\n # The KDF the KEM's own steps use (section 4.1), whatever the suite's.\n def kdf : HpkeKdf\n # RFC 9180's name for it.\n def name : String\n # Nsk: the bytes of a serialised private key.\n def private_key_size : Int32\n # Nenc and Npk, which are the same: a serialised public key.\n def public_key_size : Int32\n # Nsecret: the bytes of a shared secret, the KEM's hash's.\n def secret_size : Int32\nend\n\n# The four modes of section 5, by the byte the key schedule binds.\npub enum HpkeMode : Int32\n Base = 0\n Psk = 1\n Auth = 2\n AuthPsk = 3\nend\n\n# A KEM's private key and the public key that goes with it.\npub struct HpkePrivateKey\n # DH(sk, pk) of section 4.1 with the serialised public key *peer*: the\n # X25519 secret, or the x-coordinate of the product on P-256 and P-384.\n # An `HpkeError` for a peer of the wrong length, a point not on the\n # curve, or an all-zero X25519 secret (section 7.1.4).\n def agree(peer : String | Array(UInt8)) : Array(UInt8) | HpkeError\n # SerializePrivateKey: the 32 bytes of an X25519 key, clamped; the\n # scalar of a P-256 or P-384 key, 32 or 48 bytes big-endian.\n def bytes : Array(UInt8)\n # DeriveKeyPair (section 7.1.3): the key pair of *ikm*, at least Nsk\n # bytes, under the KEM's own KDF. For P-256 and P-384, candidates are\n # drawn until one is in [1, n - 1].\n def self.derive(kem : HpkeKem, ikm : String | Array(UInt8)) : HpkePrivateKey\n # DeserializePrivateKey: the key *bytes* are, or an `HpkeError` for the\n # wrong length or, on P-256 and P-384, a scalar not in [1, n - 1]. An\n # X25519 key is clamped (section 7.1.2).\n def self.from_bytes(kem : HpkeKem, bytes : String | Array(UInt8)) : HpkePrivateKey | HpkeError\n def initialize(kem : HpkeKem, public_key : HpkePublicKey, secret : Array(UInt8), curve : P256PrivateKey | P384PrivateKey | Nil)\n def kem : HpkeKem\n def public_key : HpkePublicKey\nend\n\n# A KEM's public key, serialised as section 7.1.1 has it.\npub struct HpkePublicKey\n # SerializePublicKey: 32 bytes for X25519, the uncompressed point for\n # P-256 (65) and P-384 (97).\n def bytes : Array(UInt8)\n # DeserializePublicKey, validated: the key *bytes* are, or an\n # `HpkeError` for the wrong length or a point not on the curve.\n def self.from_bytes(kem : HpkeKem, bytes : String | Array(UInt8)) : HpkePublicKey | HpkeError\n def initialize(kem : HpkeKem, bytes : Array(UInt8))\n def kem : HpkeKem\nend\n\n# The recipient's context (section 5.2): messages opened at sequence 0, 1,\n# 2 and on, and secrets exported, the same ones the sender's exports.\npub class HpkeRecipient\n # Skips the sequence forward to *sequence*, never back: a message lost\n # in between is not opened.\n def advance_to(sequence : UInt128) : Nil\n # Context.Export (section 5.3).\n def export(exporter_context : String | Array(UInt8), length : Int32) : Array(UInt8)\n def initialize(cipher : HpkeCipher)\n # The mode the context was set up in, by the inputs given.\n def mode : HpkeMode\n # ContextR.Open: the plaintext of *ciphertext* at the next nonce, or an\n # `HpkeError` - its tag does not match, or the sequence is at 2^96 - 1 -\n # with the sequence left where it was.\n def open(ciphertext : String | Array(UInt8), aad : String | Array(UInt8) = \"\") : String | HpkeError\n # The sequence number the next message is opened at.\n def sequence : UInt128\nend\n\n# The sender's context (section 5.2): `enc` for the recipient, then\n# messages sealed at sequence 0, 1, 2 and on, and secrets exported.\npub class HpkeSender\n # Skips the sequence forward to *sequence*, never back, so no nonce is\n # used twice: for a protocol that numbers its messages itself.\n def advance_to(sequence : UInt128) : Nil\n # The encapsulation the recipient sets its context up from.\n def enc : Array(UInt8)\n # Context.Export (section 5.3): *length* bytes, at most 255 hashes,\n # bound to *exporter_context*.\n def export(exporter_context : String | Array(UInt8), length : Int32) : Array(UInt8)\n def initialize(enc : Array(UInt8), cipher : HpkeCipher)\n # The mode the context was set up in, by the inputs given.\n def mode : HpkeMode\n # ContextS.Seal: *plaintext* sealed under the next nonce, *aad*\n # authenticated with it; or an `HpkeError` once the sequence is at\n # 2^96 - 1, with nothing sealed.\n def seal(plaintext : String | Array(UInt8), aad : String | Array(UInt8) = \"\") : String | HpkeError\n # The sequence number the next message is sealed at.\n def sequence : UInt128\nend\n\n# A KEM, a KDF and an AEAD: what the two ends agree on before anything is\n# sealed, and what every function of sections 5 and 6 runs under.\npub struct HpkeSuite\n def aead : HpkeAead\n # The suite named by its three code points, or an `HpkeError` naming the\n # one this module does not have.\n def self.from_ids(kem_id : Int32, kdf_id : Int32, aead_id : Int32) : HpkeSuite | HpkeError\n def initialize(kem : HpkeKem, kdf : HpkeKdf, aead : HpkeAead)\n def kdf : HpkeKdf\n def kem : HpkeKem\n # Single-shot Open (section 6.1): the plaintext, or an `HpkeError`.\n def open(key : HpkePrivateKey, enc : String | Array(UInt8), info : String | Array(UInt8), aad : String | Array(UInt8), ciphertext : String | Array(UInt8), psk : String | Array(UInt8) | Nil = nil, psk_id : String | Array(UInt8) | Nil = nil, sender_public : HpkePublicKey | Nil = nil) : String | HpkeError\n # Single-shot ReceiveExport (section 6.2): what `send_export`\n # exported, or an `HpkeError` when *enc* is refused.\n def receive_export(key : HpkePrivateKey, enc : String | Array(UInt8), info : String | Array(UInt8), exporter_context : String | Array(UInt8), length : Int32, psk : String | Array(UInt8) | Nil = nil, psk_id : String | Array(UInt8) | Nil = nil, sender_public : HpkePublicKey | Nil = nil) : Array(UInt8) | HpkeError\n # SetupBaseR, SetupPSKR, SetupAuthR or SetupAuthPSKR (section 5.1): the\n # recipient's context for *enc*, or an `HpkeError` when *enc* is refused.\n # The mode is chosen as `sender`'s is, by *sender_public* in place of\n # the sender's key.\n def recipient(key : HpkePrivateKey, enc : String | Array(UInt8), info : String | Array(UInt8), psk : String | Array(UInt8) | Nil = nil, psk_id : String | Array(UInt8) | Nil = nil, sender_public : HpkePublicKey | Nil = nil) : HpkeRecipient | HpkeError\n # Single-shot Seal (section 6.1): enc and *plaintext* sealed, the\n # context's first message.\n def seal(recipient : HpkePublicKey, info : String | Array(UInt8), aad : String | Array(UInt8), plaintext : String | Array(UInt8), randomness : String | Array(UInt8), psk : String | Array(UInt8) | Nil = nil, psk_id : String | Array(UInt8) | Nil = nil, sender_key : HpkePrivateKey | Nil = nil) : ::Tuple(Array(UInt8), String) | HpkeError\n # Single-shot SendExport (section 6.2): enc and *length* bytes\n # exported under *exporter_context*.\n def send_export(recipient : HpkePublicKey, info : String | Array(UInt8), exporter_context : String | Array(UInt8), length : Int32, randomness : String | Array(UInt8), psk : String | Array(UInt8) | Nil = nil, psk_id : String | Array(UInt8) | Nil = nil, sender_key : HpkePrivateKey | Nil = nil) : ::Tuple(Array(UInt8), Array(UInt8)) | HpkeError\n # SetupBaseS, SetupPSKS, SetupAuthS or SetupAuthPSKS (section 5.1), by\n # which of *psk* and *psk_id*, and *sender_key*, are given: the sender's\n # context, whose `enc` goes to the recipient. An `HpkeError` when the\n # encapsulation is refused (*recipient* is an X25519 key of small order).\n def sender(recipient : HpkePublicKey, info : String | Array(UInt8), randomness : String | Array(UInt8), psk : String | Array(UInt8) | Nil = nil, psk_id : String | Array(UInt8) | Nil = nil, sender_key : HpkePrivateKey | Nil = nil) : HpkeSender | HpkeError\n def to_s : String\nend\n\nimpl Error for HpkeError\n def message : String\nend\n" }, { "name": "std/html", "path": "src/std/html.iyi", - "sourceSha256": "c0835a0e91d257c890403854675e0101ae51958f61abe453d812279515af40cc", + "sourceSha256": "1401e9af685296c118933d3bccb6999493877485adf307a6e9b036091c02f2ac", "surface": "module std/html\n\npub struct HTML\n # Escapes the five predefined HTML characters:\n # & -> &\n # < -> <\n # > -> >\n # \" -> "\n # ' -> '\n #\n # Every other byte is copied as it is, by the run: writing each byte as a\n # `Char` wrote a UTF-8 lead or continuation byte as the two-byte encoding\n # of that code point, so \"café\" came out as \"café\".\n def self.escape(string : String, io : IyiIO) : Nil\n def self.escape(string : String) : String\n def initialize\n # Returns Unicode codepoint for standard HTML named entity, or -1 if unknown.\n def self.named_entity_codepoint(name : String) : Int32\n # Unescapes HTML character references.\n # Handles:\n # - 253 standard named entities (e.g. &, ©, €); only the\n # legacy ones (&, ©, the other Latin-1 names) without the `;`\n # - Decimal numeric entities (e.g. <)\n # - Hexadecimal numeric entities (e.g. <, <)\n # - Bare & that is not an entity is preserved unchanged\n # - Unterminated entities are handled safely\n # - Out of range numeric entities are replaced with U+FFFD\n def self.unescape(string : String) : String\nend\n" }, { "name": "std/http", "path": "src/std/http.iyi", - "sourceSha256": "c7ddc59f99e543868102459ccd62385e941d6ac8e85de415eb34e30e57f71dcd", - "surface": "module std/http\n\n# A body as it arrives: from a connection, framed by its length, by chunks\n# or by the connection's close, or from a string already in hand. `read`\n# answers the next piece, \"\" once the body is done; what the other end\n# does wrong is an `HTTPError`, and a connection that fails or is\n# cancelled answers that. The first failure is kept: every read after it\n# answers the same. A body coded `gzip`, `deflate` or `zstd` is decoded\n# as it is read when the client asked for that.\n#\n# A body has a limit: past it a read answers an `HTTPError` with status\n# 413. The server's is `Server::MAX_BODY`; a client's is none.\npub class BodyReader\n # The body's coding undone from here on: `Envelope::Gzip` for gzip,\n # `Envelope::Zlib` for HTTP's deflate - which some servers send as the\n # bare stream instead, without zlib's two bytes of header, so the first\n # two bytes are looked at first, and a pair that is not a zlib header\n # (RFC 1950 §2.2: method 8, a multiple of 31) is read as the bare\n # stream. A body that does not decode is an `HTTPError` naming the\n # coding and why.\n def decode(envelope : Envelope) : Nil\n # The body's `zstd` coding (RFC 8878) undone from here on, by std/zstd's\n # `ZstdReader` as the pieces arrive, frames one after another. A frame\n # that asks for a window past *max_window* bytes is refused rather than\n # given the memory (a window outside 1 KiB to 512 MiB is a panic), and a\n # body that does not decode is an `HTTPError` naming the coding and why.\n def decode_zstd(max_window : Int32 = Zstd::WINDOW_LIMIT) : Nil\n # Reads past the rest of the body, *limit* bytes at most: whether it\n # ended within them. A server reads past what a block left unread, so\n # the next request on the connection starts where it should.\n def drain(limit : Int64) : Bool\n # Whether every byte of the body has been read, the framing's end too.\n def finished? : Bool\n # A body that arrives as an HTTP/2 stream's DATA: refused past *limit*.\n protected def self.frames(h2 : H2Connection, id : Int32, limit : Int64) : BodyReader\n def initialize(conn : Conn | ::Nil, kind : Int32, length : Int64, limit : Int64, text : String = \"\", hello : Proc(Bool) | ::Nil = nil)\n # Whether a server reading past the rest of this body, so that the next\n # request starts where it should, would close instead: its length says\n # more than *limit* bytes are left, or its client still waits for the\n # `100 Continue` its `Expect` asked for - which, written after the\n # answer, the client reads as its next request's, and the body it\n # holds back is then waited for. A chunked body's rest is not known\n # until it is read.\n def left_past?(limit : Int64) : Bool\n protected def on_frames(h2 : H2Connection, id : Int32) : Nil\n # The body's bytes as the framing delivers them, before any decoding.\n protected def raw(max : Int32) : String | Cancelled | SocketError | HTTPError\n # The next piece of the body, at most *max* bytes; \"\" when it is done.\n def read(max : Int32 = 65536) : String | Cancelled | SocketError | HTTPError\n # The rest of the body as one string, refused past *limit* bytes (413).\n # The pieces are kept as they arrive and joined once: a builder of the\n # length a head declared was made before any of it had come, and a head\n # that declared 64 MB and sent two bytes cost the server 64 MB.\n def read_all(limit : Int64 = 2147483647_i64) : String | Cancelled | SocketError | HTTPError\n protected def remember(fault : HTTPError | Cancelled | SocketError) : HTTPError | Cancelled | SocketError\n # A body that is a string already in hand.\n def self.text(text : String) : BodyReader\n # Whether the body ends where the connection does: nothing can follow\n # it on the connection.\n def to_close? : Bool\nend\n\n# What a streamed body is written to: an answer's `Response.stream`\n# block's, and a client's upload from an `IyiIO`. Each `write` goes out as\n# one chunk (RFC 9112 §7.1), or as it is where the connection's close ends\n# the body, so a block that writes a line a second is read a line a\n# second. Under a server's `compress`, each write is a gzip piece flushed\n# whole (std/compress's `Deflater`), which decodes as it arrives too. The\n# first failure is kept, and every write after it answers it; `finish`,\n# which the server calls when the block returns, writes the end.\npub class BodyWriter\n # The end of the body: the gzip trailer, and the last chunk with the\n # blank line after it. Once; the server's call after a block that\n # finished its own body writes nothing.\n def finish : Nil | Cancelled | SocketError\n def initialize(socket : IyiSocket | TlsStream | H2Out, chunked : Bool, deflater : Deflater | ::Nil = nil)\n def write(text : String) : Nil | Cancelled | SocketError\n # Bytes of the body written so far, before any framing or coding.\n def written : Int64\nend\n\n# What `HTTP.request`'s `jar:` keeps cookies in: the `Cookie` header a\n# request to a URL carries, and an answer's `Set-Cookie` lines taken in.\n# std/cookie's `CookieJar` is one; std/http itself reads no clock, so the\n# expiry of a cookie is the store's to keep.\npub trait CookieStore\n abstract def header_for(url : String) : String | ::Nil\n abstract def update(url : String, response : Response) : Nil\nend\n\npub struct HTTP\n # For a request target in absolute form (RFC 9112 §3.2.2), where its\n # authority starts: after `http://` or `https://`, in any case.\n def self.absolute_form(target : String) : Int32 | ::Nil\n # Whether a list of codings a client accepts (`Accept-Encoding`, RFC\n # 9110 §12.5.3) takes *coding*: named, or `*`, with a `q` above 0.\n def self.accepts?(list : String, coding : String) : Bool\n # A 1xx, 204 or 304: an answer with no content (RFC 9110 §15).\n def self.bodiless?(status : Int32) : Bool\n # Where a chunked body that starts at *from* ends in `text` — the byte\n # after the last chunk's terminating CRLF (trailers included) — nil when\n # it has not all arrived, or why it is not a chunked body. What\n # `decode_chunked` will then read whole.\n def self.chunked_end(text : String, from : Int32 = 0) : Int32 | String | Nil\n # How much a list of codings a client accepts wants *coding*, as its\n # `q` in thousandths: the weight it is named with, else `*`'s, else 0 -\n # and 0 refuses it.\n def self.coding_weight(list : String, coding : String) : Int32\n # A `Content-Length` as a number, or nil when it is not one. Repeated\n # lines arrive joined, and are one length only when every one says the\n # same (RFC 9112 §6.3). A length past 2147483647 is still a length, too\n # large for either side, and is read as one past it: `to_i?` answered\n # nil for it, and 3000000000 was refused as \"not a number\".\n def self.content_length(value : String) : Int64 | ::Nil\n # A control character other than a tab, or DEL: what a field value may\n # not hold (RFC 9110 §5.5), nor a request target or a reason phrase.\n def self.control?(text : String) : Bool\n # The pairs of a request's `Cookie` header, `a=1; b=2` (RFC 6265 §4.2),\n # a value's quotes taken off; the first of a name is kept, as a server\n # reads the most specific cookie first.\n def self.cookie_pairs(value : String) : Hash(String, String)\n # A chunked body (RFC 9112 §7.1) as one string: each chunk's size line\n # is hexadecimal, extensions after `;` are ignored, and a zero-length\n # chunk ends it. What follows the last chunk is trailers, which nothing\n # here reads.\n def self.decode_chunked(text : String) : String\n def self.delete(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n def self.find_headers_end(text : String, from : Int32 = 0) : Int32 | ::Nil\n # The request as it goes on the wire, whole. `Host`, `Connection` and\n # `Content-Length` are the request's own lines; a caller's header of\n # that name is refused rather than sent twice. A body's length is\n # always written for the methods a body belongs to, so `POST` with\n # nothing to say still says `Content-Length: 0`. A byte of the path past\n # 127 goes as `%` and two hex digits: a request target is ASCII (RFC\n # 9112 §3.2, RFC 3986 §2.1), and `/ü` went out as its UTF-8 bytes, raw.\n def self.format_request(method : String, path : String, host : String, body : String = \"\", headers : Hash(String, String) = {} of String => String) : String\n def self.get(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # *text* in gzip, whole: an answer's body under a server's `compress`.\n def self.gzip(text : String) : String\n # A writer whose body goes out in gzip as it is written.\n def self.gzip_writer(socket : IyiSocket | TlsStream | H2Out, chunked : Bool) : BodyWriter\n def self.has_break?(text : String) : Bool\n # Whether a list of tokens (RFC 9110 §5.6.1), its repeated lines joined\n # with \", \", holds *token*, in any case.\n def self.has_token?(list : String, token : String) : Bool\n def self.head(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The value of a chunk size's hex digits, nil for anything else or for\n # one past what an Int32 holds.\n def self.hex(text : String) : Int32 | ::Nil\n # *n* in lowercase hexadecimal: a chunk's size line.\n def self.hex_text(n : Int64) : String\n # A Host value - the `a` of `Host: a` - or an absolute-form target's\n # authority, made of what a host and its port are spelled with (RFC 3986\n # §3.2.2, §3.2.3): a name, an IPv4 address or an IPv6 one in brackets,\n # and digits after a colon. Empty is one too, for a target with no\n # authority (RFC 9112 §3.2). A blank, a `/`, an `@` - the bytes another\n # host or a path begins with - are not.\n def self.host?(text : String) : Bool\n # The endpoints *answer*'s ServiceMode records name for a client whose\n # configuration offers *alpn* by ALPN (none: it speaks http/1.1 alone),\n # to an origin on *port*, in the order to try them: a record whose\n # mandatory keys are not all ones std/dns reads, or whose protocols -\n # alpn, and http/1.1 unless no-default-alpn - hold none of the client's,\n # names none (RFC 9460 sections 7.1.2 and 8), and neither does one whose\n # target is not a host name. Each is the record's target, or its owner\n # for \".\"; its port, or *port*; its `ech` where the ECHConfigList holds\n # a config std/tls can use (RFC 9849 section 6.1), nil otherwise; and\n # *alpn* with the protocols the record's alpn lists first, in *alpn*'s\n # order otherwise.\n def self.https_endpoints(answer : SvcbAnswer, alpn : Array(String), port : Int32) : Array(HttpsEndpoint)\n def initialize\n # One request, and the block's answer to the response, its body still\n # arriving: `response.body_io.read` takes it a piece at a time, and\n # `response.body` all of it. The connection closes when the block\n # returns. A body of *body* is a string, sent with its length, or an\n # `IyiIO`, sent chunked as it is read - which a redirect that keeps the\n # method (307, 308) cannot send again, so that answer is handed to the\n # block as it is.\n #\n # The redirects followed are RFC 9110 §15.4's: 301 and 302 make a POST a\n # GET, 303 makes anything but a HEAD a GET, and 307 and 308 keep the\n # method and the body. `Authorization`, `Proxy-Authorization` and\n # `Cookie` written by the caller are not sent to another scheme, host or\n # port - `https://` to `http://` at one host included, as curl does (Go\n # strips them for another host) - and a jar's `Secure` cookies go over\n # `https://` alone. The downgrade itself is followed, as Go's client and\n # `curl -L` follow it; any other header the caller wrote, and a 307 or\n # 308's body, go on in the clear. One more than *redirects* is an\n # `HTTPError`; with 0 the first answer is the block's.\n #\n # *timeout_ms* bounds the connect, and each read and write after it -\n # a pause, not the whole exchange, as Python's `timeout` is: a body that\n # keeps arriving is read for as long as it takes. One that runs out is a\n # `SocketError` whose `timed_out?` is true. 0, the default, waits as\n # long as the server does, as Go's and Python's clients do; a client\n # that talks to servers it does not control should give one.\n #\n # *proxy* is a proxy's URL - `http://user:password@host:port`, or\n # `host:port` alone - and every request goes through it, every redirect\n # too: `http://` as an absolute-form request to the proxy (RFC 9112\n # §3.2.2), which carries `Proxy-Authorization: Basic` when the URL has\n # a user; `https://` through a tunnel the proxy opens with `CONNECT\n # host:port` (RFC 9110 §9.3.6), the credential on the CONNECT alone and\n # TLS to the server inside it, its certificate checked as without a\n # proxy. A caller's own `Proxy-Authorization` is the proxy's too: sent\n # on the CONNECT when the URL has no user, and never inside the tunnel.\n # A CONNECT answered with anything but a 2xx - 407 when the proxy wants\n # credentials, 403, 502 - is an `HTTPError` naming the proxy and its\n # answer. `http://` through a proxy is HTTP/1.1 whatever\n # *prior_knowledge* says. Which proxy, if any, a URL's environment\n # names is `proxy_for`'s, and std/http_proxy's `HTTPProxy.from_env`.\n def self.open(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true, client : HTTPClient | ::Nil = nil, timeout_ms : Int32 = 0, proxy : String | ::Nil = nil, &block : (Response -> R)) : R | Cancelled | SocketError | HTTPError forall R\n # The field lines after the first, or why they are not fields. A name\n # repeated, in any case, is one field, its values joined in order with\n # \", \" (RFC 9110 §5.3): into a Hash by name, a second line spelled the\n # same replaced the first and one spelled otherwise was an entry\n # `header` never reached, so two different `Content-Length`s were read\n # as whichever came first or last. A value with a control character in\n # it - a CR or LF that ends no line, a NUL - is refused (§5.5) rather than\n # handed to whoever reads it or echoes it into a header.\n #\n # *unfold*, for an answer: a line that begins with a blank continues the\n # field above it (obs-fold, RFC 9112 §5.2), and a user agent joins it on\n # with one space; read as a field of its own, `HTTP.get` panicked with\n # \"header without a colon: \\\" b\\\"\". A server may refuse one, and this\n # one does.\n #\n # *apart*, when given, is handed each repeated field's values by the\n # name in the answer, a line each, before they are joined.\n def self.parse_fields(lines : Array(String), unfold : Bool = false, apart : Hash(String, Array(String)) | ::Nil = nil) : Hash(String, String) | String\n # The response as it came off the wire, whole. `head` says the request\n # was a `HEAD`, whose answer carries the headers of a body it does not\n # send. An interim answer - `100 Continue`, `103 Early Hints` - is read\n # past to the final one after it (RFC 9110 §15.2). A `101` switches\n # protocols, and HTTP ends with it. What is not a response panics with\n # why: this is the text a program has in hand; the client answers the\n # same sentences as `HTTPError`s.\n def self.parse_response(text : String, head : Bool = false) : Response\n # A response's head - the text before its blank line - read: status,\n # reason, the fields, and the lines of those on more than one. A line\n # that begins with a blank continues the field above it (obs-fold, RFC\n # 9112 §5.2), which a user agent joins on with one space.\n def self.parse_response_head(text : String) : ::Tuple(Int32, String, Hash(String, String), Hash(String, Array(String)) | ::Nil) | HTTPError\n # Where *text* goes on at *from* past the empty lines there: what a\n # server skips before a request line (RFC 9112 §2.2).\n def self.past_empty_lines(text : String, from : Int32) : Int32\n def self.post(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The proxy a request for *url* goes through, given the values of\n # `HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY` - nil when it goes\n # direct. Go's `golang.org/x/net/http/httpproxy` is the rule, line for\n # line, as its documentation and tests state it:\n #\n # * `https://` takes *https_proxy*, `http://` *http_proxy*, any other\n # scheme none. A proxy written without a scheme is `http://`'s, and is\n # answered with it, ready for `open`'s *proxy*.\n # * `localhost` (in lower case: Go's comparison) and a loopback address\n # (127.0.0.0/8, ::1, the first as IPv4-mapped too) never go through a\n # proxy.\n # * *no_proxy* is a comma-separated list, each entry trimmed and read\n # without regard to case: `*` alone sends everything direct; an IP\n # address or a CIDR range (`10.0.0.0/8`, `2001:db8::/32`) matches the\n # addresses in it, IPv4 and its IPv4-mapped form alike, and a CIDR\n # range of one family never the other's; a domain matches itself and\n # every name under it (`foo.com`: `foo.com`, `bar.foo.com`, not\n # `barfoo.com`); with a leading dot, or `*.`, only the names under it\n # (`.y.com`: `x.y.com`, not `y.com`). An address or domain may carry\n # a port (`1.2.3.4:80`, `[::1]:443`, `foo.com:8080`), and matches\n # then on that port alone, the URL's own or its scheme's default.\n # An entry that is none of these (`:80`) is passed over.\n # * A host is compared as IDNA's ASCII form, as Go compares it.\n #\n # Pure: the values are the caller's, so std/http reaches no environment;\n # std/http_proxy reads them from it.\n def self.proxy_for(url : String, http_proxy : String | ::Nil, https_proxy : String | ::Nil, no_proxy : String | ::Nil = nil) : String | ::Nil\n def self.put(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The reason phrase a status carries (RFC 9110 §15), for a response the\n # server writes; a status this table does not know gets its class's.\n def self.reason_for(status : Int32) : String\n # One request and its whole answer: the answer as `open` hands it, its\n # body read. Redirects are followed, *redirects* at most; *jar* keeps\n # the cookies the answers set and sends them back; *decompress* asks\n # for gzip, deflate and zstd and undoes them. A refused or reset\n # connection answers `SocketError`; a task cancelled answers\n # `Cancelled`; what the server does wrong answers `HTTPError`. `!`\n # passes any of them up, `.or_panic` refuses it.\n def self.request(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true, timeout_ms : Int32 = 0, proxy : String | ::Nil = nil) : Response | Cancelled | SocketError | HTTPError\n # A request's head: the request line, `Host`, `Connection` (*connection*:\n # `close`, or `Upgrade`), the caller's headers, and the body's framing -\n # *length*, chunks, or none.\n def self.request_head(method : String, path : String, host : String, headers : Hash(String, String), length : Int64 | ::Nil, chunked : Bool, connection : String) : String\n # Two field names for the same field: ASCII case is the only difference\n # a name may carry.\n def self.same_name?(a : String, b : String) : Bool\n def self.split_crlf(text : String) : Array(String)\n # A status line read: the status and its reason, or why it is not one.\n def self.status_line(line : String) : ::Tuple(Int32, String) | HTTPError\n # What an `https://` request is checked against and offers: the\n # system's roots, and ALPN's `http/1.1`, unless a program sets its own -\n # roots of its own for a server it runs, say.\n def self.tls_config : TlsConfig\n def self.tls_config=(config : TlsConfig) : TlsConfig\n # A token (RFC 9110 §5.6.2): what a method or a field name is made of.\n def self.token?(text : String) : Bool\n # Without the blanks at either end (RFC 9112 §5.1: field values are\n # trimmed of the whitespace around them).\n def self.trim(s : String) : String\n # The 101 an upgrade to *protocol* is answered with, and the connection,\n # which is the caller's from then on: std/websocket's handshake. Any\n # other answer is an `HTTPError` naming it.\n #\n # *proxy* is `open`'s, and so is its credential, on the CONNECT alone;\n # but an upgrade goes through a tunnel for `http://` as for `https://`,\n # the handshake inside it in origin form. `Upgrade` belongs to the\n # connection it is sent on (RFC 9110 §7.8), and a proxy forwarding a\n # request removes `Connection` and every field it names (§7.6.1), so a\n # handshake handed to a forward proxy in absolute form reaches the\n # server, at best, as a plain GET: the gate's proxy, which drops\n # `Connection`, leaves std/websocket's server a request it answers 400.\n def self.upgrade(url : String, protocol : String, headers : Hash(String, String) = {} of String => String, timeout_ms : Int32 = 0, proxy : String | ::Nil = nil) : Upgraded | Cancelled | SocketError | HTTPError\nend\n\n# Requests that keep their connections: an `HTTPClient` holds the connections\n# its answers left open, by host and port, and the next request to that\n# server goes over one of them instead of a new one - the TCP handshake,\n# and the slow start after it, paid once rather than per request. Its\n# methods are `HTTP`'s, redirects, cookies and decoding included:\n#\n# ```\n# client = HTTPClient.new\n# client.get(\"http://127.0.0.1:8080/a\")\n# client.get(\"http://127.0.0.1:8080/b\") # the same connection\n# client.close\n# ```\n#\n# A connection goes back once its answer has been read to the end (inside\n# `open`, up to 64 KiB left unread is read past for it) and the answer\n# allows it; at most *idle* wait per server, and the rest close. A server\n# may close an idle connection whenever it likes (RFC 9112 §9.3.1), so a\n# request whose kept connection turns out closed - the write fails, or it\n# ends before a byte of an answer - is sent again once on a new one, when\n# its method is idempotent (RFC 9110 §9.2.2) and its body a string;\n# anything else answers the failure. One task at a time per request: two\n# tasks sending at once take two connections, and a client is not for\n# sharing across threads. An `https://` connection is kept under the\n# `HTTP.tls_config` it was made with, and handed only to a request made\n# under the same one (`TlsMadeUnder`): after `HTTP.tls_config=` the next\n# request makes a connection of its own, checked against the new roots.\n# A client made with *tls* uses that configuration instead of\n# `HTTP.tls_config`, and one made with *resolver* finds its hosts by it\n# instead of `Resolver.system` - an encrypted resolver, or a hosts text\n# that pins a name to an address: std/secure_dns's `DnsOverHttps` is a\n# client of its own with both, so its server is checked against the roots\n# it was given and reached at the address it was given.\n#\n# A client resumes TLS sessions (RFC 8446 §2.2): the tickets an\n# `https://` server sends are kept for its origin - scheme, host and\n# port, whichever proxy's tunnel they came through - under the TLS\n# configuration they came under, the client's own *tls* or else\n# `HTTP.tls_config` (`TlsMadeUnder`, as a kept connection is), and a new\n# connection to that origin offers the newest, each once.\n# A resumed handshake checks no certificate - the ticket stands for the\n# check the first one made - so a ticket is never offered to another\n# origin, nor under another configuration: after `HTTP.tls_config=`, or\n# roots, an identity, `now`, `min_version` or `require_staple` assigned\n# in place - in `HTTP.tls_config` or in the client's own *tls* - the next\n# connection is a full handshake under the new ones, and that origin's\n# tickets from under the old one are dropped. (std/tls's `TlsSession`\n# holds each ticket to its configuration too; this client drops them\n# where that one would only pass them over.) At most *origin_tickets* are\n# kept an origin - 4 by default, so that as many connections as *idle*\n# keeps by default can resume at once, where std/tls's server and\n# OpenSSL's send 2 a handshake - and *tickets* in all, 256 by default: as\n# many origins as that leaves room for at *origin_tickets* each, the one\n# longest unused dropped first. 0 for either keeps none, and nothing\n# resumes. `close`\n# leaves them. Nothing goes early (0-RTT), whatever a ticket allows: the\n# module's header says why. `connections_resumed`, `tickets_held` and\n# `Response#tls_resumed?` say what was kept and used. What it saves, by\n# bench/std_http_resume.iyi built --release on a 12-core machine other\n# jobs shared (medians of 200, the middle of three runs): the handshake\n# took 0.96 ms full and 0.51 resumed against std/http's server, 1.03 and\n# 0.57 against OpenSSL 3.5's s_server - and a GET on a new connection,\n# which waits about 40 ms at each iyi end that is not the handshake's,\n# 82.98 ms and 82.00, and 42.04 and 40.96.\n#\n# Over HTTP/2 - `https://` where ALPN chose `h2`, or `http://` with\n# *prior_knowledge* (h2c, RFC 9113 §3.3) - a kept connection carries the\n# next request on a new stream, until the server says GOAWAY.\n#\n# HTTPS records (RFC 9460), when `use_https_records` is set: before a new\n# connection to an `https://` origin - not through a proxy, and not for\n# an upgrade - the client asks its resolver for the origin's HTTPS\n# records (`Resolver#https_records`, AliasMode followed) and connects by\n# the first ServiceMode record it can use: one whose mandatory keys it\n# knows - the seven std/dns reads, port and no-default-alpn mandatory by\n# being there (section 9) - and whose protocols (alpn, and http/1.1\n# unless no-default-alpn) hold one of the configuration's `alpn`, or\n# http/1.1 where that is empty. Its target - the record's owner for \".\"\n# - and its port, or the origin's, are where the connection goes; its\n# `ech` is the connection's `ech_configs` (RFC 9848); and the protocols\n# it lists go first in the ClientHello's ALPN, which still offers every\n# one the configuration does (section 7.1.2: a record says which\n# protocols the endpoint has, and cannot take one away). The certificate\n# is checked, and the server named inside, for the origin's host,\n# whatever the target (section 2.3). An `ech` whose ECHConfigList holds\n# no config std/tls can use is passed over, as RFC 9849 section 6.1 has\n# it. The address hints are not read: the target's addresses are asked\n# for as any host's are.\n#\n# An endpoint that does not connect gives way to the next, then to the\n# last AliasMode target and to the origin itself (section 3) - unless\n# every endpoint carries `ech`, when the request fails rather than name\n# the server in the clear (RFC 9848 section 5.1). A TLS refusal is the\n# request's failure, but for one: a server that turned the encrypted\n# ClientHello down, its certificate checked for the config's\n# public_name, is asked once more on a new connection to the same\n# address, under the retry_configs it sent, or without ECH where it sent\n# none, which turns ECH off there (RFC 9849 section 6.1.6); a refusal of\n# that is the failure. No records (NXDOMAIN, NODATA), a set section 2.2\n# refuses, an AliasMode \".\" (the service says it is not there), or no\n# record the client can use: the connection is made as without records.\n# So is one whose lookup failed through a resolver that asks in the\n# clear, as section 3.1 lets a client choose; through encrypted\n# upstreams the failure is the request's, as section 3.1 asks, for\n# there the one who can drop the answer is an attacker taking the\n# records away. `localhost` and addresses are not asked about, and an\n# `http://` URL is not made `https://` by its records (section 9.5's\n# SHOULD): the program wrote the scheme.\n#\n# A record's `ech` and protocols make a configuration of their own - a\n# copy of the client's, made once and kept for the origin while the same\n# ones come under the same configuration - so that a kept connection and\n# a ticket are handed on only under the record they were made by\n# (`TlsMadeUnder` holds `ech_configs`). Off by default: every new\n# connection then waits on one DNS answer or more before its ClientHello\n# (RFC 9848 section 5.3 forbids sending it sooner), with no cache to\n# spare the next, and the records may move a connection to another host\n# and port, which is the program's to ask for.\npub class HTTPClient\n # Every idle connection closed. The client may be used after; it opens\n # new ones.\n def close : Nil\n # Connections this client has opened, and requests sent over one it\n # kept: what a program reads to see that its connections are kept.\n def connections_made : Int32\n # Connections this client opened whose TLS handshake resumed a session\n # by a ticket it kept.\n def connections_resumed : Int32\n def connections_reused : Int32\n def delete(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n def get(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n protected def give_back(key : String, conn : Conn) : Nil\n def head(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The configuration a connection to *origin* is made under by a record\n # whose `ech` is *ech* (nil: none) and whose protocols put *alpn* in its\n # order, *base* being the client's own or `HTTP.tls_config`: *base*\n # itself where those are its own, and otherwise a copy of it with them -\n # the same object every time while *base* is the same (`TlsMadeUnder`),\n # so that a connection kept and a ticket held under it serve the next\n # connection by the same record. It becomes the one the origin's kept\n # connections are taken under (`https_current`). The origin's\n # configurations from under another *base* go.\n protected def https_config(origin : String, base : TlsConfig, ech : Array(UInt8) | ::Nil, alpn : Array(String)) : TlsConfig\n # The configuration *origin*'s last connection by HTTPS records was made\n # under, while *base* is the one it came from - *base* itself where it\n # was made as without records: what a kept connection to the origin\n # must have been made under to be taken. Nil when none was made by them\n # under *base*, and then none is taken.\n protected def https_current(origin : String, base : TlsConfig) : TlsConfig | ::Nil\n # *timeout_ms* and *proxy* are `HTTP.open`'s, for every request the\n # client sends; *tls* and *resolver* are this client's own (above);\n # *origin_tickets* and *tickets* bound the TLS tickets kept, by origin\n # and in all.\n def initialize(idle : Int32 = 4, timeout_ms : Int32 = 0, prior_knowledge : Bool = false, proxy : String | ::Nil = nil, tls : TlsConfig | ::Nil = nil, resolver : Resolver | ::Nil = nil, origin_tickets : Int32 = 4, tickets : Int32 = 256)\n protected def made : Nil\n def open(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true, &block : (Response -> R)) : R | Cancelled | SocketError | HTTPError forall R\n def post(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # `http://` spoken as HTTP/2 from the first byte, without asking: for a\n # server known to take it.\n def prior_knowledge : Bool\n # The proxy every request goes through, `HTTP.open`'s *proxy*; its\n # connections are kept under it.\n def proxy : String | ::Nil\n def put(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n def request(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true) : Response | Cancelled | SocketError | HTTPError\n # What turns a host name into addresses for this client's connections,\n # instead of `Resolver.system`: encrypted upstreams, or a hosts text\n # alone that pins a name to an address, as std/secure_dns's\n # `DnsOverHttps` pins its server's.\n def resolver : Resolver | ::Nil\n protected def resumed : Nil\n # The session a new connection to *origin* under *tls* offers a ticket\n # from and keeps the server's new ones in; nil when the client keeps\n # none. The origin's tickets from under another configuration are\n # dropped, not left to take room that `TlsSession` would never offer\n # them from. The session handed out is the newest used; past the room\n # *tickets* leaves, the oldest goes.\n protected def session_for(origin : String, tls : TlsConfig) : TlsSession | ::Nil\n # A kept connection to *key*, nil when none waits. An HTTP/2 connection\n # that can take no more requests is closed rather than handed out, and\n # so is an `https://` one made under another TLS configuration than\n # *tls*, the one the request is made under (`TlsMadeUnder`): keyed by\n # scheme, host and port alone, a connection whose server was checked\n # against one set of roots carried requests after `HTTP.tls_config=`\n # named others that would have refused it.\n protected def take(key : String, tls : TlsConfig | ::Nil = nil) : Conn | ::Nil\n # The TLS tickets held for *origin* - `https://host:port`, the host in\n # lower case and the port written even where it is 443 - or for every\n # origin when nil.\n def tickets_held(origin : String | ::Nil = nil) : Int32\n # What this client's `https://` requests are checked against and offer,\n # instead of `HTTP.tls_config`: roots of its own, or ALPN's `h2` first.\n def tls : TlsConfig | ::Nil\n # Whether a new `https://` connection is made by the origin's HTTPS\n # records (the class's header); off by default.\n def use_https_records : Bool\n # Whether a new `https://` connection is made by the origin's HTTPS\n # records (the class's header); off by default.\n def use_https_records=(value : Bool) : Bool\nend\n\n# What the other end did that HTTP does not allow, as a value: `reason` is\n# the sentence, `status` the answer a server gives a request that did it\n# (400, or 413 and 431 for the limits). `message` says it is HTTP's.\npub struct HTTPError\n def initialize(reason : String, status : Int32 = 400)\n def reason : String\n def status : Int32\n def to_s : String\nend\n\n# One endpoint an origin's HTTPS records name, as `HTTP.https_endpoints`\n# reads it: the host and port to connect to, the ECHConfigList to encrypt\n# the ClientHello under (nil: none), and the protocols to offer by ALPN,\n# in order.\npub struct HttpsEndpoint\n def alpn : Array(String)\n def ech : Array(UInt8) | ::Nil\n def host : String\n def initialize(host : String, port : Int32, ech : Array(UInt8) | ::Nil, alpn : Array(String))\n def port : Int32\nend\n\n# A `multipart/form-data` body (RFC 7578) read a part at a time, as it\n# arrives: `next_part` answers the next `Part`, nil after the last, and a\n# part's `read` answers its bytes up to the boundary, \"\" at its end. A part\n# not read to its end is read past by the next `next_part`. The body of a\n# request is `Request#multipart`'s; any `BodyReader` and boundary will do.\n# A part's head is refused past 16 KiB, and a body that ends before its\n# closing boundary is an `HTTPError`.\npub class Multipart\n # The boundary a `Content-Type` of `multipart/...` names, nil when it\n # names none - or one of a length RFC 2046 §5.1.1 does not allow, 1 to\n # 70 characters, which `new` refuses with a panic. The type is the\n # peer's: `Request#multipart` handed a client's 90-character boundary\n # through to that panic (found by bench/std_fuzz_exercise.sh).\n def self.boundary(content_type : String) : String | ::Nil\n # Where *pattern* is in what is held, -1 when it is not there yet.\n protected def find(pattern : String) : Int32\n def initialize(body : BodyReader, boundary : String)\n # The next part, nil after the last.\n def next_part : Part | Nil | Cancelled | SocketError | HTTPError\n # Up to *max* bytes of the part being read, \"\" at its boundary. What is\n # held back is what may yet be the start of the boundary.\n protected def part_read(max : Int32) : String | Cancelled | SocketError | HTTPError\n # One more piece of the body onto what is held: false at its end.\n protected def pull : Bool | Cancelled | SocketError | HTTPError\nend\n\n# A multipart/form-data body to send: fields and files in order. Its\n# boundary is chosen when the body is first asked for - `finish`,\n# `content_type` or `boundary`, whichever comes first, after which no\n# part is added - as `iyi-` and 32 hex digits of the parts' SHA-1, made\n# again from itself in the rare case a part holds it: so it is none of\n# the parts' bytes by construction rather than by chance, the same\n# fields make the same body, and std/http reaches no source of\n# randomness for it. A boundary given to `new` is used as it is, and a\n# part that holds it is refused.\npub class MultipartBuilder\n def boundary : String\n def content_type : String\n def field(name : String, value : String) : self\n def file(name : String, filename : String, data : String, content_type : String = MIME.from_extension(filename)) : self\n # The body, closing boundary and all.\n def finish : String\n # The boundary ends a part where it follows a CRLF, the part's first\n # bytes included, which follow the head's blank line.\n protected def self.holds?(data : String, mark : String) : Bool\n def initialize(boundary : String | ::Nil = nil)\n # A name inside quotes, as browsers write one: a quote and a line break\n # percent-encoded (the WHATWG form encoding), so no field can end the\n # parameter or the line early.\n protected def self.quoted(text : String) : String\nend\n\n# One part of a multipart body: its head's fields, its name and file name\n# from `Content-Disposition`, and its bytes, read up to its boundary.\npub class Part\n def content_type : String | ::Nil\n # The file name a browser sent the part under, `filename=`.\n def filename : String | ::Nil\n def finished? : Bool\n def header(name : String) : String | ::Nil\n def headers : Hash(String, String)\n def initialize(owner : Multipart, headers : Hash(String, String))\n # The form field's name, `name=` of `Content-Disposition`.\n def name : String | ::Nil\n # The part's next bytes, at most *max*; \"\" at its end.\n def read(max : Int32 = 65536) : String | Cancelled | SocketError | HTTPError\n # The rest of the part as one string, refused past *limit* bytes.\n def read_all(limit : Int64 = 2147483647_i64) : String | Cancelled | SocketError | HTTPError\nend\n\n# What a server's block is handed: one request. Its body is `body`, read\n# whole before the block ran, or under `stream_bodies` read by the block\n# from `body_io` as it arrives; `body` then reads the rest itself, and a\n# body that cannot be read - the client went, or sent one that is not\n# HTTP - ends the connection with that sentence rather than be answered as\n# if it were whole.\npub class Request\n def body : String\n def body_io : BodyReader\n # The pairs of the request's `Cookie` header, the first of a name kept.\n def cookies : Hash(String, String)\n # The fields of a form: the query's, and an\n # `application/x-www-form-urlencoded` body's after them. The body is\n # read for it.\n def form : Params\n def header(name : String) : String | ::Nil\n def headers : Hash(String, String)\n # A request whose body is in hand: what `parse_request` answers.\n def initialize(method : String, path : String, query : String | ::Nil, headers : Hash(String, String), body : String, version : String = \"HTTP/1.1\", remote_address : String | ::Nil = nil)\n # A request whose body is still on the connection.\n def initialize(method : String, path : String, query : String | ::Nil, headers : Hash(String, String), version : String, reader : BodyReader, remote_address : String | ::Nil = nil)\n def method : String\n # The body as `multipart/form-data` parts, read as they arrive; nil when\n # the `Content-Type` is not multipart or names no boundary\n # (`Multipart.boundary`).\n def multipart : Multipart | ::Nil\n def path : String\n # The client's certificate chain, leaf first, as std/tls checked it:\n # under `serve(tls:)` with `client_roots` set, the certificate the\n # client presented; empty otherwise.\n def peer_certificates : Array(Certificate)\n # The client's certificate chain, leaf first, as std/tls checked it:\n # under `serve(tls:)` with `client_roots` set, the certificate the\n # client presented; empty otherwise.\n def peer_certificates=(value : Array(Certificate)) : Array(Certificate)\n def query : String | ::Nil\n # The client's address, as the server's socket saw it (no port): nil\n # for a request a program made, or when the client had already gone. A\n # proxy in front is what this names; `X-Forwarded-For` is the proxy's\n # word, and a header any client can write.\n def remote_address : String | ::Nil\n def tls_resumed=(resumed : Bool) : Bool\n # Whether the TLS connection the request came over resumed a session by\n # a ticket, under `serve(tls:)`: its `peer_certificates` are then the\n # ticket's. False in the clear.\n def tls_resumed? : Bool\n def version : String\nend\n\n# A response: what a server's block answers, and what a client is handed.\n#\n# To write one: `Response.new(200, \"hello\")`, a status and a string;\n# `Response.new(200, io)`, a stream the server copies out and closes, with\n# the caller's `Content-Length` when it gives one and chunked otherwise;\n# `Response.stream(200) { |w| w.write(\"...\") }`, whose block writes as it\n# goes; `Response.upgrade(headers) { |socket, leftover| ... }`, a 101 after\n# which the connection is the block's. `add_header` writes a field on a\n# line of its own, as each `Set-Cookie` must be.\n#\n# What a client is handed: the status, reason and headers, and the body -\n# `body` whole, or `body_io` as it arrives inside `HTTP.open`'s block.\npub class Response\n # One more line of the field *name*, written on a line of its own: what\n # two `Set-Cookie`s need, since the \", \" a field's lines are joined with\n # is part of a cookie's date.\n def add_header(name : String, value : String) : Nil\n # The lines of each field written on more than one, by its name in\n # `headers`; nil when every field is on one line.\n def apart : Hash(String, Array(String)) | ::Nil\n # The body: whole. Inside `HTTP.open`'s block it is read here, the first\n # time it is asked for, and a body that cannot be read panics with why;\n # `body_io` answers that as a value.\n def body : String\n # The body as it arrives. For a response a program made, its string.\n def body_io : BodyReader\n # Whether the client undid the body's `gzip`, `deflate` or `zstd`\n # coding; the `Content-Encoding` and `Content-Length` that described it\n # are gone from the headers then.\n def decoded? : Bool\n # A header by name, as the server spelled it or otherwise: a field\n # name is case-insensitive (RFC 9110 §5.1), and `content-length` is as\n # common on the wire as `Content-Length`.\n def header(name : String) : String | ::Nil\n # A field's lines apart, in the order they came: `header` joins them\n # with \", \", and `Set-Cookie` is the field that join breaks (RFC 9110\n # §5.3), since a cookie's `Expires` date has a comma in it - two cookies\n # read as \"a=1; Expires=Wed, 21 Oct 2026 07:28:00 GMT, b=2\", which no\n # split takes back apart. Empty for a field that is not there.\n def header_values(name : String) : Array(String)\n def headers : Hash(String, String)\n # A response a client read: its body comes from *reader*, and *url* is\n # where it came from.\n def initialize(status : Int32, reason : String, headers : Hash(String, String), apart : Hash(String, Array(String)) | ::Nil, reader : BodyReader, url : String, decoded : Bool)\n def initialize(status : Int32, reason : String, headers : Hash(String, String), body : String, apart : Hash(String, Array(String)) | ::Nil = nil)\n # A response to write: the reason is the status's own, the headers the\n # caller's. `Response.new(200, \"hello\")` is the common shape.\n def initialize(status : Int32, body : String = \"\", headers : Hash(String, String) = {} of String => String)\n # A response whose body is *source*, copied out by the server and\n # closed after.\n def initialize(status : Int32, source : IyiIO, headers : Hash(String, String) = {} of String => String)\n # The body read whole into the response, which is answered; why not,\n # when it cannot be.\n def load : Response | Cancelled | SocketError | HTTPError\n def reason : String\n def source : IyiIO | ::Nil\n def status : Int32\n # A response whose body the block writes as it goes, chunked.\n def self.stream(status : Int32, headers : Hash(String, String) = {} of String => String, &writer : (BodyWriter -> Nil)) : Response\n def success? : Bool\n def takeover : Proc(IyiSocket | TlsStream, String, Nil) | ::Nil\n protected def takeover=(takeover : Proc(IyiSocket | TlsStream, String, Nil)) : Proc(IyiSocket | TlsStream, String, Nil)\n def tls_resumed=(resumed : Bool) : Bool\n # Whether the `https://` connection the answer came over resumed a TLS\n # session by a ticket an `HTTPClient` kept, rather than a full\n # handshake; false in the clear and for one a program made.\n def tls_resumed? : Bool\n # A 101 Switching Protocols with *headers* (`Upgrade` and the\n # protocol's own), after which the connection is the block's: it is\n # handed the connection - the socket, or under `serve(tls:)` the\n # `TlsStream` over it - and the bytes the client sent after its request,\n # and the connection closes when it returns.\n def self.upgrade(headers : Hash(String, String), &takeover : (IyiSocket | TlsStream, String -> Nil)) : Response\n # The URL the answer came from, after any redirects; nil for one a\n # program made.\n def url : String | ::Nil\n def writer : Proc(BodyWriter, Nil) | ::Nil\n protected def writer=(writer : Proc(BodyWriter, Nil)) : Proc(BodyWriter, Nil)\nend\n\npub struct Server\n # The headers a coded body goes with: the coding, and `Vary` so that a\n # cache keeps the answers apart. A `Content-Length` the block gave is\n # the plain body's, and goes.\n protected def self.coded(response : Response, coding : String) : Response\n # The coding *response* goes in to the client of *req*, or nil: one the\n # client takes (RFC 9110 §12.5.3, a `q` of 0 refusing it), for an\n # answer with a body and a type worth compressing, and no coding of its\n # own. A whole body goes `zstd` when the client's `q` for it is at least\n # its `q` for gzip, and `gzip` otherwise. The tie goes to zstd for the\n # server's time: measured on this machine, release builds, on\n # src/std/http.iyi (199,134 bytes) `Gzip.compress` wrote 54,484 bytes in\n # 5 ms and `Zstd.compress` 61,753 in 1 ms, and on CHANGELOG.md (1.5 MB)\n # 552,446 in 58 ms against 589,785 in 13 ms - a quarter of the time, for\n # an eighth more bytes at most. A stream - an `IyiIO` or a\n # `Response.stream` - goes gzip or as it is: std/compress's `Deflater`\n # writes each piece as it comes, where std/zstd's compressor takes its\n # input whole, and a frame a write would code each piece alone.\n def self.coding_for(req : Request, response : Response) : String | ::Nil\n # A string body compressed whole in *coding*, `zstd` or `gzip`, with\n # the headers that say so. `Zstd.compress`'s frame needs a window of\n # 1 MiB at most, inside the 8 MB RFC 9659 §3 holds an encoder to.\n protected def self.compressed(response : Response, coding : String) : Response\n # A response's head. `Content-Length` is *length*'s - the caller's own\n # line is checked against it, and kept only where no body follows it: a\n # HEAD, or a 304 that names the length a 200 would have (RFC 9110 §8.6) -\n # or `Transfer-Encoding: chunked` when *chunked*, or neither for a body\n # the connection's close ends. `Connection` says what the connection will\n # do next, except on an *upgrade*, whose own line says it. A 1xx, 204 or\n # 304 has no content (RFC 9110 §15), and writes neither a body nor a\n # length of its own (§8.6): the body was read by the client as the start\n # of the next answer, and `Content-Length: 0` on a 304 told a cache that\n # the resource had become empty. A field added on lines of its own\n # (`add_header`) is written a line each.\n def self.format_head(response : Response, head : Bool, close : Bool, length : Int64 | ::Nil, chunked : Bool, upgrade : Bool = false) : String\n # The response as it goes on the wire, whole: `format_head` for its\n # string body, and the body.\n def self.format_response(response : Response, head : Bool, close : Bool) : String\n # One connection: requests until the client is done. A request that is\n # not HTTP is answered with why and the connection closed, since the\n # bytes after it cannot be trusted to be a request either. Requests that\n # came in one read are read where they lie, one after another. A\n # connection that opens with HTTP/2's preface is served as HTTP/2. Under\n # *tls* the handshake comes first, and the connection ends with\n # close_notify. A connection this side wrote last on - an answer, a\n # refusal, a GOAWAY - closes by lingering (`Conn#close`), so the reset\n # a close with the client's bytes unread is cannot take what was\n # written from the client before it reads it. One that timed out or was\n # cancelled, with nothing of this side's waiting to be read, closes at\n # once, and so does one whose client said `close` and sent nothing past\n # its request (`serve_h1`).\n def self.handle(client : IyiSocket, handler : Proc(Request, Response), listener : IyiSocket | ServeStop | Nil = nil, idle : Hash(UInt64, Conn) | ::Nil = nil, stream_bodies : Bool = false, compress : Bool = false, timeout_ms : Int32 = 0, tls : TlsServerConfig | TlsFrozenServerConfig | Nil = nil, request_timeout_ms : Int32 = 0) : Nil\n def initialize\n # A request's head - the text before its blank line - read and checked,\n # or why it is not one.\n def self.parse_head(text : String) : RequestHead | HTTPError\n # A request at *from* in `text`, if a whole one is there. No request and\n # no error means \"not yet\". `consumed` counts from the start of `text`.\n def self.parse_request(text : String, from : Int32 = 0) : ParsedRequest\n # Serve `listener` until it is closed, on *workers* threads: each\n # connection is a task on the pool `spawn_parallel` starts (SPEC.md\n # III.4.13), started by whichever worker is free and served there to\n # its end, while the thread that called this accepts. The block runs on\n # the connection's worker, once per request, on several workers at\n # once; what it answers is written back.\n # The close ends a connection waiting on its client too, for its next\n # request or for the rest of one, and one that is answering ends when its\n # answer is written: a keep-alive client that sat idle kept `serve` from\n # returning for as long as it stayed, and so did one that had sent\n # what has arrived of a request so far - one byte of it was enough.\n #\n # *stream_bodies*: the block reads each request's body from `body_io`\n # rather than being handed it whole. *compress*: an answer whose type\n # std/mime calls compressible goes zstd or gzip to a client that\n # accepts one (`coding_for`).\n #\n # *timeout_ms*: a request's head must have arrived within it, counted\n # from when the server starts waiting for one - a kept-alive connection's\n # idle time included - and no read of a body nor write of an answer may\n # wait longer; a connection that does is closed. Without it, a client\n # that sends a byte of its head every few seconds (Slowloris) holds a\n # connection and its fiber for as long as it likes, and so does one that\n # reads none of its answer. A minute by default, nginx's\n # `client_header_timeout`; 0 for none. A connection handed over by\n # `Response.upgrade` waits as its protocol likes.\n #\n # *request_timeout_ms*: the whole request - head and body - must have\n # arrived within it, counted from the same moment as *timeout_ms*, as\n # Go's `ReadTimeout` beside its `ReadHeaderTimeout`; the head is held to\n # the sooner of the two. Without it a body sent a byte every few seconds\n # was bounded per read alone, and held its connection for as long as it\n # dripped. A block that reads the body itself (*stream_bodies*) reads\n # under it until the block returns, and what it leaves unread is read\n # past under it too. Over HTTP/2 the wait for each request and its body\n # is held to it on the connection the same way, the head to the sooner\n # of the two. Five minutes by default, which a 64 MiB body (`MAX_BODY`)\n # meets at 224 KB/s; a server taking large uploads over slow links\n # raises it. 0 for none. A connection past it is closed, unanswered, as\n # one past *timeout_ms* is.\n #\n # *max_connections*: at most this many connections are served at once;\n # 1024 by default, 0 for no limit. Past it the server stops accepting\n # until one ends, so the next connection waits in the kernel's listen\n # queue - queued, not refused, as Go's `netutil.LimitListener` does - and\n # is served in its turn; a client that gives up waiting goes unserved.\n # Each served connection holds its place until it closes, a kept-alive\n # one between requests too, which *timeout_ms* bounds. The process's\n # own limit on descriptors may come first.\n #\n # *tls*: each connection is TLS (std/tls's `TlsServer`, 1.3 or 1.2) under this\n # configuration, its handshake held to *timeout_ms* too and run on the\n # worker that serves it; one whose handshake fails is closed, the\n # client told why by an alert. Inside it a connection that opens with\n # HTTP/2's preface - as a client whose ALPN chose `h2` does, when\n # `tls.alpn` offers it - is served HTTP/2, and any other HTTP/1.1. A\n # `Response.upgrade` block over TLS is handed the `TlsStream`, so a\n # WebSocket is `wss://`. On more than one worker the config is a\n # `TlsFrozenServerConfig` - `config.frozen` - since every worker reads\n # it at once: its ticket keys are one set behind a lock, so a ticket a\n # connection on one worker was sent resumes on any other, and an\n # identity source given to `frozen` is a block the compiler checked. A\n # `TlsServerConfig` itself is refused at the call (\"Server.serve hands\n # `tls : TlsServerConfig` to other threads, several at once, and it is\n # not Share: ... Pass `config.frozen` ...\"), not frozen here: `frozen`\n # is a copy, so a property assigned after the server started would be\n # served by no worker and nothing would say so, and a config with an\n # `identity_source` cannot be frozen without its source given again as\n # a block the compiler checks. Under `workers: 1` either is served.\n #\n # *workers*: the pool's size, `IyiWorkers.count` by default - `IYI_WORKERS`\n # from the environment, else the cores this process may run on. The\n # pool is the process's one, and it does not grow or shrink once it has\n # started, so a count other than the one it started with panics. Every\n # worker runs the block at once, so the compiler checks it where it is\n # written as it checks a parallel task's (`@[Share]`, SPEC.md III.4.4):\n # every value it captures must be `Share` - a table behind a `Mutex`\n # (std/mutex), a count in an `Atomic`, a `SharedRouter` (std/router) -\n # every captured local assigned once, and the constants and class\n # variables it names `Share`. The refusal names the variable, why its\n # type is not `Share`, and the two ways out: the lock, or `workers: 1`.\n # `workers: 1`, written as the literal, serves on the calling thread\n # alone - each connection a task beside the accept loop, as `spawn`\n # makes one - and its block is not checked: a block that calls a\n # `Router`, appends to an `Array` or keeps a handle that is not `Share`\n # is served there. A count in a variable is checked whatever it holds,\n # since the compiler cannot know it is 1. A count of 1 from the default\n # (`IYI_WORKERS=1`, one core) serves on the calling thread too.\n #\n # A connection stays on the worker that started it (III.4.13, part 1):\n # connections spread over the workers free as they arrive, and one kept\n # alive is not moved later. A block that computes without parking holds\n # its worker meanwhile, as any task does. The listener is the calling\n # thread's, and a close of it is made there: a block that stops the\n # server sends on a `Channel` a task beside the server receives from,\n # and that task closes it. At the stop each connection waiting on its\n # client is closed on its own worker, sent close_notify there first\n # under *tls*.\n def self.serve(listener : IyiSocket, stream_bodies : Bool = false, compress : Bool = false, timeout_ms : Int32 = 60000, tls : C = nil, request_timeout_ms : Int32 = REQUEST_TIMEOUT, max_connections : Int32 = MAX_CONNECTIONS, workers : Int32 = IyiWorkers.count, &handler : (Request -> Response)) : Nil forall C\n # `serve` with a value for its handler rather than a block: a\n # `SharedProc`, a `SharedRouter` (std/router), or one of the program's\n # own, whose `call(request : Request) : Response` answers each request,\n # as Go's `http.Handler` does. On more than one worker it must be\n # `Share` (SPEC.md III.4.4), since every connection on every worker\n # calls the one value at once: the compiler refuses one that is not at\n # the argument, naming the field that failed and the two ways out - a\n # count in an `Atomic`, a pool or a cache behind a `Mutex`, a\n # `SharedRouter` for a `Router`, or `workers: 1`, under which any value\n # with that `call` is served on the calling thread, a `Router`\n # included. What the check does not see is the edge `spawn_parallel`\n # has too: a method the handler calls that writes a module's state. A\n # struct handler is copied into each connection's task, and an `Atomic`\n # in it with it: what the connections share is a class, or held by\n # one.\n #\n # The rest is the block form's: *stream_bodies*, *compress*,\n # *timeout_ms*, *tls* (`config.frozen` on more than one worker),\n # *request_timeout_ms*, *max_connections* and *workers*, HTTP/2 by its\n # preface, `Response.upgrade`, and a close of *listener* - on the\n # thread that called this - ending every connection waiting on its\n # client and returning once the others have answered.\n def self.serve(listener : IyiSocket, handler : H, stream_bodies : Bool = false, compress : Bool = false, timeout_ms : Int32 = 60000, tls : C = nil, request_timeout_ms : Int32 = REQUEST_TIMEOUT, max_connections : Int32 = MAX_CONNECTIONS, workers : Int32 = IyiWorkers.count) : Nil forall H, C\n protected def self.writer_for(socket : IyiSocket | TlsStream, chunked : Bool, gzip : Bool) : BodyWriter\nend\n\n# A TLS connection used as a socket is: read into a buffer, written to,\n# closed, its timeouts set - by a `Conn`, a `BodyWriter`, and a connection\n# handed over by an upgrade (`wss://` at either end). What TLS refuses is a\n# `SocketError` carrying TLS's own sentence (`layer_reason`), so answers\n# keep the types they have over a plain socket.\npub class TlsStream\n # The socket closed at once, nothing sent or read first.\n def abort : Nil\n # close_notify, the sending half shut, and what the peer still sends\n # read past for a bounded time before the socket closes\n # (`TlsConnection#close`).\n def close : Nil\n # close_notify and the sending half shut; reads go on until the peer's\n # own (`TlsConnection#close_write`).\n def close_write : SocketError | ::Nil\n def closed? : Bool\n def initialize(tls : TlsConnection, host : String)\n # The peer's certificate chain, leaf first, as the handshake checked it:\n # the server's, at a client; the client's, at a server that asked.\n def peer_certificates : Array(Certificate)\n # Up to *count* bytes into *buffer*: 0 once the peer closed with\n # close_notify.\n def read(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n # Up to *max_bytes* as a string, what is held first: \"\" once the peer\n # closed with close_notify. `IyiSocket#read`'s form, for a block an\n # upgrade hands either.\n def read(max_bytes : Int32 = 4096) : String | Cancelled | SocketError\n # How long a read or a write of the connection under it may wait.\n def read_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\n # Whether the handshake resumed a session (`TlsConnection#resumed?`).\n def resumed? : Bool\n def write(text : String) : Int32 | Cancelled | SocketError\n def write_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\nend\n\n# What `HTTP.upgrade` answers: the 101, and the connection, which is the\n# caller's now - the socket, and the bytes the server sent after its head.\npub class Upgraded\n def initialize(response : Response, socket : IyiSocket | TlsStream, leftover : String)\n def leftover : String\n def response : Response\n # The connection: the socket, or for `https://` the `TlsStream` over it.\n def socket : IyiSocket | TlsStream\nend\n\nimpl Error for HTTPError\n def message : String\nend\n" + "sourceSha256": "64b03ff04cf05a0b7915ba30233f4194d25d78d2a26c44a934c3dc50361a2133", + "surface": "module std/http\n\n# A body as it arrives: from a connection, framed by its length, by chunks\n# or by the connection's close, or from a string already in hand. `read`\n# answers the next piece, \"\" once the body is done; what the other end\n# does wrong is an `HTTPError`, and a connection that fails or is\n# cancelled answers that. The first failure is kept: every read after it\n# answers the same. A body coded `gzip`, `deflate` or `zstd` is decoded\n# as it is read when the client asked for that.\n#\n# A body has a limit: past it a read answers an `HTTPError` with status\n# 413. The server's is `Server::MAX_BODY`; a client's is none.\npub class BodyReader\n # The body's coding undone from here on: `Envelope::Gzip` for gzip,\n # `Envelope::Zlib` for HTTP's deflate - which some servers send as the\n # bare stream instead, without zlib's two bytes of header, so the first\n # two bytes are looked at first, and a pair that is not a zlib header\n # (RFC 1950 §2.2: method 8, a multiple of 31) is read as the bare\n # stream. A body that does not decode is an `HTTPError` naming the\n # coding and why.\n def decode(envelope : Envelope) : Nil\n # The body's `zstd` coding (RFC 8878) undone from here on, by std/zstd's\n # `ZstdReader` as the pieces arrive, frames one after another. A frame\n # that asks for a window past *max_window* bytes is refused rather than\n # given the memory (a window outside 1 KiB to 512 MiB is a panic), and a\n # body that does not decode is an `HTTPError` naming the coding and why.\n def decode_zstd(max_window : Int32 = Zstd::WINDOW_LIMIT) : Nil\n # Reads past the rest of the body, *limit* bytes at most: whether it\n # ended within them. A server reads past what a block left unread, so\n # the next request on the connection starts where it should.\n def drain(limit : Int64) : Bool\n # Whether every byte of the body has been read, the framing's end too.\n def finished? : Bool\n # A body that arrives as an HTTP/2 stream's DATA: refused past *limit*.\n protected def self.frames(h2 : H2Connection, id : Int32, limit : Int64) : BodyReader\n def initialize(conn : Conn | ::Nil, kind : Int32, length : Int64, limit : Int64, text : String = \"\", hello : Proc(Bool) | ::Nil = nil)\n # Whether a server reading past the rest of this body, so that the next\n # request starts where it should, would close instead: its length says\n # more than *limit* bytes are left, or its client still waits for the\n # `100 Continue` its `Expect` asked for - which, written after the\n # answer, the client reads as its next request's, and the body it\n # holds back is then waited for. A chunked body's rest is not known\n # until it is read.\n def left_past?(limit : Int64) : Bool\n protected def on_frames(h2 : H2Connection, id : Int32) : Nil\n # The body's bytes as the framing delivers them, before any decoding.\n protected def raw(max : Int32) : String | Cancelled | SocketError | HTTPError\n # The next piece of the body, at most *max* bytes; \"\" when it is done.\n def read(max : Int32 = 65536) : String | Cancelled | SocketError | HTTPError\n # The rest of the body as one string, refused past *limit* bytes (413).\n # The pieces are kept as they arrive and joined once: a builder of the\n # length a head declared was made before any of it had come, and a head\n # that declared 64 MB and sent two bytes cost the server 64 MB.\n def read_all(limit : Int64 = 2147483647_i64) : String | Cancelled | SocketError | HTTPError\n protected def remember(fault : HTTPError | Cancelled | SocketError) : HTTPError | Cancelled | SocketError\n # A body that is a string already in hand.\n def self.text(text : String) : BodyReader\n # Whether the body ends where the connection does: nothing can follow\n # it on the connection.\n def to_close? : Bool\nend\n\n# What a streamed body is written to: an answer's `Response.stream`\n# block's, and a client's upload from an `IyiIO`. Each `write` goes out as\n# one chunk (RFC 9112 §7.1), or as it is where the connection's close ends\n# the body, so a block that writes a line a second is read a line a\n# second. Under a server's `compress`, each write is a gzip piece flushed\n# whole (std/compress's `Deflater`), which decodes as it arrives too. The\n# first failure is kept, and every write after it answers it; `finish`,\n# which the server calls when the block returns, writes the end.\npub class BodyWriter\n # The end of the body: the gzip trailer, and the last chunk with the\n # blank line after it. Once; the server's call after a block that\n # finished its own body writes nothing.\n def finish : Nil | Cancelled | SocketError\n def initialize(socket : IyiSocket | TlsStream | H2Out, chunked : Bool, deflater : Deflater | ::Nil = nil)\n def write(text : String) : Nil | Cancelled | SocketError\n # Bytes of the body written so far, before any framing or coding.\n def written : Int64\nend\n\n# What `HTTP.request`'s `jar:` keeps cookies in: the `Cookie` header a\n# request to a URL carries, and an answer's `Set-Cookie` lines taken in.\n# std/cookie's `CookieJar` is one; std/http itself reads no clock, so the\n# expiry of a cookie is the store's to keep.\npub trait CookieStore\n abstract def header_for(url : String) : String | ::Nil\n abstract def update(url : String, response : Response) : Nil\nend\n\npub struct HTTP\n # For a request target in absolute form (RFC 9112 §3.2.2), where its\n # authority starts: after `http://` or `https://`, in any case.\n def self.absolute_form(target : String) : Int32 | ::Nil\n # Whether a list of codings a client accepts (`Accept-Encoding`, RFC\n # 9110 §12.5.3) takes *coding*: named, or `*`, with a `q` above 0.\n def self.accepts?(list : String, coding : String) : Bool\n # A 1xx, 204 or 304: an answer with no content (RFC 9110 §15).\n def self.bodiless?(status : Int32) : Bool\n # Where a chunked body that starts at *from* ends in `text` — the byte\n # after the last chunk's terminating CRLF (trailers included) — nil when\n # it has not all arrived, or why it is not a chunked body. What\n # `decode_chunked` will then read whole.\n def self.chunked_end(text : String, from : Int32 = 0) : Int32 | String | Nil\n # How much a list of codings a client accepts wants *coding*, as its\n # `q` in thousandths: the weight it is named with, else `*`'s, else 0 -\n # and 0 refuses it.\n def self.coding_weight(list : String, coding : String) : Int32\n # A `Content-Length` as a number, or nil when it is not one. Repeated\n # lines arrive joined, and are one length only when every one says the\n # same (RFC 9112 §6.3). A length past 2147483647 is still a length, too\n # large for either side, and is read as one past it: `to_i?` answered\n # nil for it, and 3000000000 was refused as \"not a number\".\n def self.content_length(value : String) : Int64 | ::Nil\n # A control character other than a tab, or DEL: what a field value may\n # not hold (RFC 9110 §5.5), nor a request target or a reason phrase.\n def self.control?(text : String) : Bool\n # The pairs of a request's `Cookie` header, `a=1; b=2` (RFC 6265 §4.2),\n # a value's quotes taken off; the first of a name is kept, as a server\n # reads the most specific cookie first.\n def self.cookie_pairs(value : String) : Hash(String, String)\n # A chunked body (RFC 9112 §7.1) as one string: each chunk's size line\n # is hexadecimal, extensions after `;` are ignored, and a zero-length\n # chunk ends it. What follows the last chunk is trailers, which nothing\n # here reads.\n def self.decode_chunked(text : String) : String\n def self.delete(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n def self.find_headers_end(text : String, from : Int32 = 0) : Int32 | ::Nil\n # The request as it goes on the wire, whole. `Host`, `Connection` and\n # `Content-Length` are the request's own lines; a caller's header of\n # that name is refused rather than sent twice. A body's length is\n # always written for the methods a body belongs to, so `POST` with\n # nothing to say still says `Content-Length: 0`. A byte of the path past\n # 127 goes as `%` and two hex digits: a request target is ASCII (RFC\n # 9112 §3.2, RFC 3986 §2.1), and `/ü` went out as its UTF-8 bytes, raw.\n def self.format_request(method : String, path : String, host : String, body : String = \"\", headers : Hash(String, String) = {} of String => String) : String\n def self.get(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # *text* in gzip, whole: an answer's body under a server's `compress`.\n def self.gzip(text : String) : String\n # A writer whose body goes out in gzip as it is written.\n def self.gzip_writer(socket : IyiSocket | TlsStream | H2Out, chunked : Bool) : BodyWriter\n def self.has_break?(text : String) : Bool\n # Whether a list of tokens (RFC 9110 §5.6.1), its repeated lines joined\n # with \", \", holds *token*, in any case.\n def self.has_token?(list : String, token : String) : Bool\n def self.head(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The value of a chunk size's hex digits, nil for anything else or for\n # one past what an Int32 holds.\n def self.hex(text : String) : Int32 | ::Nil\n # *n* in lowercase hexadecimal: a chunk's size line.\n def self.hex_text(n : Int64) : String\n # A Host value - the `a` of `Host: a` - or an absolute-form target's\n # authority, made of what a host and its port are spelled with (RFC 3986\n # §3.2.2, §3.2.3): a name, an IPv4 address or an IPv6 one in brackets,\n # and digits after a colon. Empty is one too, for a target with no\n # authority (RFC 9112 §3.2). A blank, a `/`, an `@` - the bytes another\n # host or a path begins with - are not.\n def self.host?(text : String) : Bool\n # The endpoints *answer*'s ServiceMode records name for a client whose\n # configuration offers *alpn* by ALPN (none: it speaks http/1.1 alone),\n # to an origin on *port*, in the order to try them: a record whose\n # mandatory keys are not all ones std/dns reads, or whose protocols -\n # alpn, and http/1.1 unless no-default-alpn - hold none of the client's,\n # names none (RFC 9460 sections 7.1.2 and 8), and neither does one whose\n # target is not a host name. Each is the record's target, or its owner\n # for \".\"; its port, or *port*; its `ech` where the ECHConfigList holds\n # a config std/tls can use (RFC 9849 section 6.1), nil otherwise; and\n # *alpn* with the protocols the record's alpn lists first, in *alpn*'s\n # order otherwise.\n def self.https_endpoints(answer : SvcbAnswer, alpn : Array(String), port : Int32) : Array(HttpsEndpoint)\n def initialize\n # One request, and the block's answer to the response, its body still\n # arriving: `response.body_io.read` takes it a piece at a time, and\n # `response.body` all of it. The connection closes when the block\n # returns. A body of *body* is a string, sent with its length, or an\n # `IyiIO`, sent chunked as it is read - which a redirect that keeps the\n # method (307, 308) cannot send again, so that answer is handed to the\n # block as it is.\n #\n # The redirects followed are RFC 9110 §15.4's: 301 and 302 make a POST a\n # GET, 303 makes anything but a HEAD a GET, and 307 and 308 keep the\n # method and the body. `Authorization`, `Proxy-Authorization` and\n # `Cookie` written by the caller are not sent to another scheme, host or\n # port - `https://` to `http://` at one host included, as curl does (Go\n # strips them for another host) - and a jar's `Secure` cookies go over\n # `https://` alone. The downgrade itself is followed, as Go's client and\n # `curl -L` follow it; any other header the caller wrote, and a 307 or\n # 308's body, go on in the clear. One more than *redirects* is an\n # `HTTPError`; with 0 the first answer is the block's.\n #\n # *timeout_ms* bounds the connect, and each read and write after it -\n # a pause, not the whole exchange, as Python's `timeout` is: a body that\n # keeps arriving is read for as long as it takes. One that runs out is a\n # `SocketError` whose `timed_out?` is true. 0, the default, waits as\n # long as the server does, as Go's and Python's clients do; a client\n # that talks to servers it does not control should give one.\n #\n # *proxy* is a proxy's URL - `http://user:password@host:port`, or\n # `host:port` alone - and every request goes through it, every redirect\n # too: `http://` as an absolute-form request to the proxy (RFC 9112\n # §3.2.2), which carries `Proxy-Authorization: Basic` when the URL has\n # a user; `https://` through a tunnel the proxy opens with `CONNECT\n # host:port` (RFC 9110 §9.3.6), the credential on the CONNECT alone and\n # TLS to the server inside it, its certificate checked as without a\n # proxy. A caller's own `Proxy-Authorization` is the proxy's too: sent\n # on the CONNECT when the URL has no user, and never inside the tunnel.\n # A CONNECT answered with anything but a 2xx - 407 when the proxy wants\n # credentials, 403, 502 - is an `HTTPError` naming the proxy and its\n # answer. `http://` through a proxy is HTTP/1.1 whatever\n # *prior_knowledge* says. Which proxy, if any, a URL's environment\n # names is `proxy_for`'s, and std/http_proxy's `HTTPProxy.from_env`.\n def self.open(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true, client : HTTPClient | ::Nil = nil, timeout_ms : Int32 = 0, proxy : String | ::Nil = nil, &block : (Response -> R)) : R | Cancelled | SocketError | HTTPError forall R\n # The field lines after the first, or why they are not fields. A name\n # repeated, in any case, is one field, its values joined in order with\n # \", \" (RFC 9110 §5.3): into a Hash by name, a second line spelled the\n # same replaced the first and one spelled otherwise was an entry\n # `header` never reached, so two different `Content-Length`s were read\n # as whichever came first or last. A value with a control character in\n # it - a CR or LF that ends no line, a NUL - is refused (§5.5) rather than\n # handed to whoever reads it or echoes it into a header.\n #\n # *unfold*, for an answer: a line that begins with a blank continues the\n # field above it (obs-fold, RFC 9112 §5.2), and a user agent joins it on\n # with one space; read as a field of its own, `HTTP.get` panicked with\n # \"header without a colon: \\\" b\\\"\". A server may refuse one, and this\n # one does.\n #\n # *apart*, when given, is handed each repeated field's values by the\n # name in the answer, a line each, before they are joined.\n def self.parse_fields(lines : Array(String), unfold : Bool = false, apart : Hash(String, Array(String)) | ::Nil = nil) : Hash(String, String) | String\n # The response as it came off the wire, whole. `head` says the request\n # was a `HEAD`, whose answer carries the headers of a body it does not\n # send. An interim answer - `100 Continue`, `103 Early Hints` - is read\n # past to the final one after it (RFC 9110 §15.2). A `101` switches\n # protocols, and HTTP ends with it. What is not a response panics with\n # why: this is the text a program has in hand; the client answers the\n # same sentences as `HTTPError`s.\n def self.parse_response(text : String, head : Bool = false) : Response\n # A response's head - the text before its blank line - read: status,\n # reason, the fields, and the lines of those on more than one. A line\n # that begins with a blank continues the field above it (obs-fold, RFC\n # 9112 §5.2), which a user agent joins on with one space.\n def self.parse_response_head(text : String) : ::Tuple(Int32, String, Hash(String, String), Hash(String, Array(String)) | ::Nil) | HTTPError\n # Where *text* goes on at *from* past the empty lines there: what a\n # server skips before a request line (RFC 9112 §2.2).\n def self.past_empty_lines(text : String, from : Int32) : Int32\n def self.post(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The proxy a request for *url* goes through, given the values of\n # `HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY` - nil when it goes\n # direct. Go's `golang.org/x/net/http/httpproxy` is the rule, line for\n # line, as its documentation and tests state it:\n #\n # * `https://` takes *https_proxy*, `http://` *http_proxy*, any other\n # scheme none. A proxy written without a scheme is `http://`'s, and is\n # answered with it, ready for `open`'s *proxy*.\n # * `localhost` (in lower case: Go's comparison) and a loopback address\n # (127.0.0.0/8, ::1, the first as IPv4-mapped too) never go through a\n # proxy.\n # * *no_proxy* is a comma-separated list, each entry trimmed and read\n # without regard to case: `*` alone sends everything direct; an IP\n # address or a CIDR range (`10.0.0.0/8`, `2001:db8::/32`) matches the\n # addresses in it, IPv4 and its IPv4-mapped form alike, and a CIDR\n # range of one family never the other's; a domain matches itself and\n # every name under it (`foo.com`: `foo.com`, `bar.foo.com`, not\n # `barfoo.com`); with a leading dot, or `*.`, only the names under it\n # (`.y.com`: `x.y.com`, not `y.com`). An address or domain may carry\n # a port (`1.2.3.4:80`, `[::1]:443`, `foo.com:8080`), and matches\n # then on that port alone, the URL's own or its scheme's default.\n # An entry that is none of these (`:80`) is passed over.\n # * A host is compared as IDNA's ASCII form, as Go compares it.\n #\n # Pure: the values are the caller's, so std/http reaches no environment;\n # std/http_proxy reads them from it.\n def self.proxy_for(url : String, http_proxy : String | ::Nil, https_proxy : String | ::Nil, no_proxy : String | ::Nil = nil) : String | ::Nil\n def self.put(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The reason phrase a status carries (RFC 9110 §15), for a response the\n # server writes; a status this table does not know gets its class's.\n def self.reason_for(status : Int32) : String\n # One request and its whole answer: the answer as `open` hands it, its\n # body read. Redirects are followed, *redirects* at most; *jar* keeps\n # the cookies the answers set and sends them back; *decompress* asks\n # for gzip, deflate and zstd and undoes them. A refused or reset\n # connection answers `SocketError`; a task cancelled answers\n # `Cancelled`; what the server does wrong answers `HTTPError`. `!`\n # passes any of them up, `.or_panic` refuses it.\n def self.request(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true, timeout_ms : Int32 = 0, proxy : String | ::Nil = nil) : Response | Cancelled | SocketError | HTTPError\n # A request's head: the request line, `Host`, `Connection` (*connection*:\n # `close`, or `Upgrade`), the caller's headers, and the body's framing -\n # *length*, chunks, or none.\n def self.request_head(method : String, path : String, host : String, headers : Hash(String, String), length : Int64 | ::Nil, chunked : Bool, connection : String) : String\n # Two field names for the same field: ASCII case is the only difference\n # a name may carry.\n def self.same_name?(a : String, b : String) : Bool\n def self.split_crlf(text : String) : Array(String)\n # A status line read: the status and its reason, or why it is not one.\n def self.status_line(line : String) : ::Tuple(Int32, String) | HTTPError\n # What an `https://` request is checked against and offers: the\n # system's roots, and ALPN's `http/1.1`, unless a program sets its own -\n # roots of its own for a server it runs, say.\n def self.tls_config : TlsConfig\n def self.tls_config=(config : TlsConfig) : TlsConfig\n # A token (RFC 9110 §5.6.2): what a method or a field name is made of.\n def self.token?(text : String) : Bool\n # Without the blanks at either end (RFC 9112 §5.1: field values are\n # trimmed of the whitespace around them).\n def self.trim(s : String) : String\n # The 101 an upgrade to *protocol* is answered with, and the connection,\n # which is the caller's from then on: std/websocket's handshake. Any\n # other answer is an `HTTPError` naming it.\n #\n # *proxy* is `open`'s, and so is its credential, on the CONNECT alone;\n # but an upgrade goes through a tunnel for `http://` as for `https://`,\n # the handshake inside it in origin form. `Upgrade` belongs to the\n # connection it is sent on (RFC 9110 §7.8), and a proxy forwarding a\n # request removes `Connection` and every field it names (§7.6.1), so a\n # handshake handed to a forward proxy in absolute form reaches the\n # server, at best, as a plain GET: the gate's proxy, which drops\n # `Connection`, leaves std/websocket's server a request it answers 400.\n def self.upgrade(url : String, protocol : String, headers : Hash(String, String) = {} of String => String, timeout_ms : Int32 = 0, proxy : String | ::Nil = nil) : Upgraded | Cancelled | SocketError | HTTPError\nend\n\n# Requests that keep their connections: an `HTTPClient` holds the connections\n# its answers left open, by host and port, and the next request to that\n# server goes over one of them instead of a new one - the TCP handshake,\n# and the slow start after it, paid once rather than per request. Its\n# methods are `HTTP`'s, redirects, cookies and decoding included:\n#\n# ```\n# client = HTTPClient.new\n# client.get(\"http://127.0.0.1:8080/a\")\n# client.get(\"http://127.0.0.1:8080/b\") # the same connection\n# client.close\n# ```\n#\n# A connection goes back once its answer has been read to the end (inside\n# `open`, up to 64 KiB left unread is read past for it) and the answer\n# allows it; at most *idle* wait per server, and the rest close. A server\n# may close an idle connection whenever it likes (RFC 9112 §9.3.1), so a\n# request whose kept connection turns out closed - the write fails, or it\n# ends before a byte of an answer - is sent again once on a new one, when\n# its method is idempotent (RFC 9110 §9.2.2) and its body a string;\n# anything else answers the failure. One task at a time per request: two\n# tasks sending at once take two connections, and a client is not for\n# sharing across threads. An `https://` connection is kept under the\n# `HTTP.tls_config` it was made with, and handed only to a request made\n# under the same one (`TlsMadeUnder`): after `HTTP.tls_config=` the next\n# request makes a connection of its own, checked against the new roots.\n# A client made with *tls* uses that configuration instead of\n# `HTTP.tls_config`, and one made with *resolver* finds its hosts by it\n# instead of `Resolver.system` - an encrypted resolver, or a hosts text\n# that pins a name to an address: std/secure_dns's `DnsOverHttps` is a\n# client of its own with both, so its server is checked against the roots\n# it was given and reached at the address it was given.\n#\n# A client resumes TLS sessions (RFC 8446 §2.2): the tickets an\n# `https://` server sends are kept for its origin - scheme, host and\n# port, whichever proxy's tunnel they came through - under the TLS\n# configuration they came under, the client's own *tls* or else\n# `HTTP.tls_config` (`TlsMadeUnder`, as a kept connection is), and a new\n# connection to that origin offers the newest, each once.\n# A resumed handshake checks no certificate - the ticket stands for the\n# check the first one made - so a ticket is never offered to another\n# origin, nor under another configuration: after `HTTP.tls_config=`, or\n# roots, an identity, `now`, `min_version` or `require_staple` assigned\n# in place - in `HTTP.tls_config` or in the client's own *tls* - the next\n# connection is a full handshake under the new ones, and that origin's\n# tickets from under the old one are dropped. (std/tls's `TlsSession`\n# holds each ticket to its configuration too; this client drops them\n# where that one would only pass them over.) At most *origin_tickets* are\n# kept an origin - 4 by default, so that as many connections as *idle*\n# keeps by default can resume at once, where std/tls's server and\n# OpenSSL's send 2 a handshake - and *tickets* in all, 256 by default: as\n# many origins as that leaves room for at *origin_tickets* each, the one\n# longest unused dropped first. 0 for either keeps none, and nothing\n# resumes. `close`\n# leaves them. Nothing goes early (0-RTT), whatever a ticket allows: the\n# module's header says why. `connections_resumed`, `tickets_held` and\n# `Response#tls_resumed?` say what was kept and used. What it saves, by\n# bench/std_http_resume.iyi built --release on a 12-core machine other\n# jobs shared (medians of 200, the middle of three runs): the handshake\n# took 0.96 ms full and 0.51 resumed against std/http's server, 1.03 and\n# 0.57 against OpenSSL 3.5's s_server - and a GET on a new connection,\n# which waits about 40 ms at each iyi end that is not the handshake's,\n# 82.98 ms and 82.00, and 42.04 and 40.96.\n#\n# Over HTTP/2 - `https://` where ALPN chose `h2`, or `http://` with\n# *prior_knowledge* (h2c, RFC 9113 §3.3) - a kept connection carries the\n# next request on a new stream, until the server says GOAWAY.\n#\n# HTTPS records (RFC 9460), when `use_https_records` is set: before a new\n# connection to an `https://` origin - not through a proxy, and not for\n# an upgrade - the client asks its resolver for the origin's HTTPS\n# records (`Resolver#https_records`, AliasMode followed) and connects by\n# the first ServiceMode record it can use: one whose mandatory keys it\n# knows - the seven std/dns reads, port and no-default-alpn mandatory by\n# being there (section 9) - and whose protocols (alpn, and http/1.1\n# unless no-default-alpn) hold one of the configuration's `alpn`, or\n# http/1.1 where that is empty. Its target - the record's owner for \".\"\n# - and its port, or the origin's, are where the connection goes; its\n# `ech` is the connection's `ech_configs` (RFC 9848); and the protocols\n# it lists go first in the ClientHello's ALPN, which still offers every\n# one the configuration does (section 7.1.2: a record says which\n# protocols the endpoint has, and cannot take one away). The certificate\n# is checked, and the server named inside, for the origin's host,\n# whatever the target (section 2.3). An `ech` whose ECHConfigList holds\n# no config std/tls can use is passed over, as RFC 9849 section 6.1 has\n# it. The address hints are not read: the target's addresses are asked\n# for as any host's are.\n#\n# An endpoint that does not connect gives way to the next, then to the\n# last AliasMode target and to the origin itself (section 3) - unless\n# every endpoint carries `ech`, when the request fails rather than name\n# the server in the clear (RFC 9848 section 5.1). A TLS refusal is the\n# request's failure, but for one: a server that turned the encrypted\n# ClientHello down, its certificate checked for the config's\n# public_name, is asked once more on a new connection to the same\n# address, under the retry_configs it sent, or without ECH where it sent\n# none, which turns ECH off there (RFC 9849 section 6.1.6); a refusal of\n# that is the failure. No records (NXDOMAIN, NODATA), a set section 2.2\n# refuses, an AliasMode \".\" (the service says it is not there), or no\n# record the client can use: the connection is made as without records.\n# So is one whose lookup failed through a resolver that asks in the\n# clear, as section 3.1 lets a client choose; through encrypted\n# upstreams the failure is the request's, as section 3.1 asks, for\n# there the one who can drop the answer is an attacker taking the\n# records away. `localhost` and addresses are not asked about, and an\n# `http://` URL is not made `https://` by its records (section 9.5's\n# SHOULD): the program wrote the scheme.\n#\n# A record's `ech` and protocols make a configuration of their own - a\n# copy of the client's, made once and kept for the origin while the same\n# ones come under the same configuration - so that a kept connection and\n# a ticket are handed on only under the record they were made by\n# (`TlsMadeUnder` holds `ech_configs`). Off by default: every new\n# connection then waits on one DNS answer or more before its ClientHello\n# (RFC 9848 section 5.3 forbids sending it sooner), with no cache to\n# spare the next, and the records may move a connection to another host\n# and port, which is the program's to ask for.\npub class HTTPClient\n # Every idle connection closed. The client may be used after; it opens\n # new ones.\n def close : Nil\n # Connections this client has opened, and requests sent over one it\n # kept: what a program reads to see that its connections are kept.\n def connections_made : Int32\n # Connections this client opened whose TLS handshake resumed a session\n # by a ticket it kept.\n def connections_resumed : Int32\n def connections_reused : Int32\n def delete(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n def get(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n protected def give_back(key : String, conn : Conn) : Nil\n def head(url : String, headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # The configuration a connection to *origin* is made under by a record\n # whose `ech` is *ech* (nil: none) and whose protocols put *alpn* in its\n # order, *base* being the client's own or `HTTP.tls_config`: *base*\n # itself where those are its own, and otherwise a copy of it with them -\n # the same object every time while *base* is the same (`TlsMadeUnder`),\n # so that a connection kept and a ticket held under it serve the next\n # connection by the same record. It becomes the one the origin's kept\n # connections are taken under (`https_current`). The origin's\n # configurations from under another *base* go.\n protected def https_config(origin : String, base : TlsConfig, ech : Array(UInt8) | ::Nil, alpn : Array(String)) : TlsConfig\n # The configuration *origin*'s last connection by HTTPS records was made\n # under, while *base* is the one it came from - *base* itself where it\n # was made as without records: what a kept connection to the origin\n # must have been made under to be taken. Nil when none was made by them\n # under *base*, and then none is taken.\n protected def https_current(origin : String, base : TlsConfig) : TlsConfig | ::Nil\n # *timeout_ms* and *proxy* are `HTTP.open`'s, for every request the\n # client sends; *tls* and *resolver* are this client's own (above);\n # *origin_tickets* and *tickets* bound the TLS tickets kept, by origin\n # and in all.\n def initialize(idle : Int32 = 4, timeout_ms : Int32 = 0, prior_knowledge : Bool = false, proxy : String | ::Nil = nil, tls : TlsConfig | ::Nil = nil, resolver : Resolver | ::Nil = nil, origin_tickets : Int32 = 4, tickets : Int32 = 256)\n protected def made : Nil\n def open(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true, &block : (Response -> R)) : R | Cancelled | SocketError | HTTPError forall R\n def post(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n # `http://` spoken as HTTP/2 from the first byte, without asking: for a\n # server known to take it.\n def prior_knowledge : Bool\n # The proxy every request goes through, `HTTP.open`'s *proxy*; its\n # connections are kept under it.\n def proxy : String | ::Nil\n def put(url : String, body : String | IyiIO = \"\", headers : Hash(String, String) = {} of String => String) : Response | Cancelled | SocketError | HTTPError\n def request(method : String, url : String, headers : Hash(String, String) = {} of String => String, body : String | IyiIO = \"\", redirects : Int32 = 10, jar : CookieStore | ::Nil = nil, decompress : Bool = true) : Response | Cancelled | SocketError | HTTPError\n # What turns a host name into addresses for this client's connections,\n # instead of `Resolver.system`: encrypted upstreams, or a hosts text\n # alone that pins a name to an address, as std/secure_dns's\n # `DnsOverHttps` pins its server's.\n def resolver : Resolver | ::Nil\n protected def resumed : Nil\n # The session a new connection to *origin* under *tls* offers a ticket\n # from and keeps the server's new ones in; nil when the client keeps\n # none. The origin's tickets from under another configuration are\n # dropped, not left to take room that `TlsSession` would never offer\n # them from. The session handed out is the newest used; past the room\n # *tickets* leaves, the oldest goes.\n protected def session_for(origin : String, tls : TlsConfig) : TlsSession | ::Nil\n # A kept connection to *key*, nil when none waits. An HTTP/2 connection\n # that can take no more requests is closed rather than handed out, and\n # so is an `https://` one made under another TLS configuration than\n # *tls*, the one the request is made under (`TlsMadeUnder`): keyed by\n # scheme, host and port alone, a connection whose server was checked\n # against one set of roots carried requests after `HTTP.tls_config=`\n # named others that would have refused it.\n protected def take(key : String, tls : TlsConfig | ::Nil = nil) : Conn | ::Nil\n # The TLS tickets held for *origin* - `https://host:port`, the host in\n # lower case and the port written even where it is 443 - or for every\n # origin when nil.\n def tickets_held(origin : String | ::Nil = nil) : Int32\n # What this client's `https://` requests are checked against and offer,\n # instead of `HTTP.tls_config`: roots of its own, or ALPN's `h2` first.\n def tls : TlsConfig | ::Nil\n # Whether a new `https://` connection is made by the origin's HTTPS\n # records (the class's header); off by default.\n def use_https_records : Bool\n # Whether a new `https://` connection is made by the origin's HTTPS\n # records (the class's header); off by default.\n def use_https_records=(value : Bool) : Bool\nend\n\n# What the other end did that HTTP does not allow, as a value: `reason` is\n# the sentence, `status` the answer a server gives a request that did it\n# (400, or 413 and 431 for the limits). `message` says it is HTTP's.\npub struct HTTPError\n def initialize(reason : String, status : Int32 = 400)\n def reason : String\n def status : Int32\n def to_s : String\nend\n\n# One endpoint an origin's HTTPS records name, as `HTTP.https_endpoints`\n# reads it: the host and port to connect to, the ECHConfigList to encrypt\n# the ClientHello under (nil: none), and the protocols to offer by ALPN,\n# in order.\npub struct HttpsEndpoint\n def alpn : Array(String)\n def ech : Array(UInt8) | ::Nil\n def host : String\n def initialize(host : String, port : Int32, ech : Array(UInt8) | ::Nil, alpn : Array(String))\n def port : Int32\nend\n\n# A `multipart/form-data` body (RFC 7578) read a part at a time, as it\n# arrives: `next_part` answers the next `Part`, nil after the last, and a\n# part's `read` answers its bytes up to the boundary, \"\" at its end. A part\n# not read to its end is read past by the next `next_part`. The body of a\n# request is `Request#multipart`'s; any `BodyReader` and boundary will do.\n# A part's head is refused past 16 KiB, and a body that ends before its\n# closing boundary is an `HTTPError`.\npub class Multipart\n # The boundary a `Content-Type` of `multipart/...` names, nil when it\n # names none - or one of a length RFC 2046 §5.1.1 does not allow, 1 to\n # 70 characters, which `new` refuses with a panic. The type is the\n # peer's: `Request#multipart` handed a client's 90-character boundary\n # through to that panic (found by bench/std_fuzz_exercise.sh).\n def self.boundary(content_type : String) : String | ::Nil\n # Where *pattern* is in what is held, -1 when it is not there yet.\n protected def find(pattern : String) : Int32\n def initialize(body : BodyReader, boundary : String)\n # The next part, nil after the last.\n def next_part : Part | Nil | Cancelled | SocketError | HTTPError\n # Up to *max* bytes of the part being read, \"\" at its boundary. What is\n # held back is what may yet be the start of the boundary.\n protected def part_read(max : Int32) : String | Cancelled | SocketError | HTTPError\n # One more piece of the body onto what is held: false at its end.\n protected def pull : Bool | Cancelled | SocketError | HTTPError\nend\n\n# A multipart/form-data body to send: fields and files in order. Its\n# boundary is chosen when the body is first asked for - `finish`,\n# `content_type` or `boundary`, whichever comes first, after which no\n# part is added - as `iyi-` and 32 hex digits of the parts' SHA-1, made\n# again from itself in the rare case a part holds it: so it is none of\n# the parts' bytes by construction rather than by chance, the same\n# fields make the same body, and std/http reaches no source of\n# randomness for it. A boundary given to `new` is used as it is, and a\n# part that holds it is refused.\npub class MultipartBuilder\n def boundary : String\n def content_type : String\n def field(name : String, value : String) : self\n def file(name : String, filename : String, data : String, content_type : String = MIME.from_extension(filename)) : self\n # The body, closing boundary and all.\n def finish : String\n # The boundary ends a part where it follows a CRLF, the part's first\n # bytes included, which follow the head's blank line.\n protected def self.holds?(data : String, mark : String) : Bool\n def initialize(boundary : String | ::Nil = nil)\n # A name inside quotes, as browsers write one: a quote and a line break\n # percent-encoded (the WHATWG form encoding), so no field can end the\n # parameter or the line early.\n protected def self.quoted(text : String) : String\nend\n\n# One part of a multipart body: its head's fields, its name and file name\n# from `Content-Disposition`, and its bytes, read up to its boundary.\npub class Part\n def content_type : String | ::Nil\n # The file name a browser sent the part under, `filename=`.\n def filename : String | ::Nil\n def finished? : Bool\n def header(name : String) : String | ::Nil\n def headers : Hash(String, String)\n def initialize(owner : Multipart, headers : Hash(String, String))\n # The form field's name, `name=` of `Content-Disposition`.\n def name : String | ::Nil\n # The part's next bytes, at most *max*; \"\" at its end.\n def read(max : Int32 = 65536) : String | Cancelled | SocketError | HTTPError\n # The rest of the part as one string, refused past *limit* bytes.\n def read_all(limit : Int64 = 2147483647_i64) : String | Cancelled | SocketError | HTTPError\nend\n\n# What a server's block is handed: one request. Its body is `body`, read\n# whole before the block ran, or under `stream_bodies` read by the block\n# from `body_io` as it arrives; `body` then reads the rest itself, and a\n# body that cannot be read - the client went, or sent one that is not\n# HTTP - ends the connection with that sentence rather than be answered as\n# if it were whole.\npub class Request\n def body : String\n def body_io : BodyReader\n # The pairs of the request's `Cookie` header, the first of a name kept.\n def cookies : Hash(String, String)\n # The fields of a form: the query's, and an\n # `application/x-www-form-urlencoded` body's after them. The body is\n # read for it.\n def form : Params\n def header(name : String) : String | ::Nil\n def headers : Hash(String, String)\n # A request whose body is in hand: what `parse_request` answers.\n def initialize(method : String, path : String, query : String | ::Nil, headers : Hash(String, String), body : String, version : String = \"HTTP/1.1\", remote_address : String | ::Nil = nil)\n # A request whose body is still on the connection.\n def initialize(method : String, path : String, query : String | ::Nil, headers : Hash(String, String), version : String, reader : BodyReader, remote_address : String | ::Nil = nil)\n def method : String\n # The body as `multipart/form-data` parts, read as they arrive; nil when\n # the `Content-Type` is not multipart or names no boundary\n # (`Multipart.boundary`).\n def multipart : Multipart | ::Nil\n def path : String\n # The client's certificate chain, leaf first, as std/tls checked it:\n # under `serve(tls:)` with `client_roots` set, the certificate the\n # client presented; empty otherwise.\n def peer_certificates : Array(Certificate)\n # The client's certificate chain, leaf first, as std/tls checked it:\n # under `serve(tls:)` with `client_roots` set, the certificate the\n # client presented; empty otherwise.\n def peer_certificates=(value : Array(Certificate)) : Array(Certificate)\n def query : String | ::Nil\n # The client's address, as the server's socket saw it (no port): nil\n # for a request a program made, or when the client had already gone. A\n # proxy in front is what this names; `X-Forwarded-For` is the proxy's\n # word, and a header any client can write.\n def remote_address : String | ::Nil\n def tls_resumed=(resumed : Bool) : Bool\n # Whether the TLS connection the request came over resumed a session by\n # a ticket, under `serve(tls:)`: its `peer_certificates` are then the\n # ticket's. False in the clear.\n def tls_resumed? : Bool\n def version : String\nend\n\n# A response: what a server's block answers, and what a client is handed.\n#\n# To write one: `Response.new(200, \"hello\")`, a status and a string;\n# `Response.new(200, io)`, a stream the server copies out and closes, with\n# the caller's `Content-Length` when it gives one and chunked otherwise;\n# `Response.stream(200) { |w| w.write(\"...\") }`, whose block writes as it\n# goes; `Response.upgrade(headers) { |socket, leftover| ... }`, a 101 after\n# which the connection is the block's. `add_header` writes a field on a\n# line of its own, as each `Set-Cookie` must be.\n#\n# What a client is handed: the status, reason and headers, and the body -\n# `body` whole, or `body_io` as it arrives inside `HTTP.open`'s block.\npub class Response\n # One more line of the field *name*, written on a line of its own: what\n # two `Set-Cookie`s need, since the \", \" a field's lines are joined with\n # is part of a cookie's date.\n def add_header(name : String, value : String) : Nil\n # The lines of each field written on more than one, by its name in\n # `headers`; nil when every field is on one line.\n def apart : Hash(String, Array(String)) | ::Nil\n # The body: whole. Inside `HTTP.open`'s block it is read here, the first\n # time it is asked for, and a body that cannot be read panics with why;\n # `body_io` answers that as a value.\n def body : String\n # The body as it arrives. For a response a program made, its string.\n def body_io : BodyReader\n # Whether the client undid the body's `gzip`, `deflate` or `zstd`\n # coding; the `Content-Encoding` and `Content-Length` that described it\n # are gone from the headers then.\n def decoded? : Bool\n # A header by name, as the server spelled it or otherwise: a field\n # name is case-insensitive (RFC 9110 §5.1), and `content-length` is as\n # common on the wire as `Content-Length`.\n def header(name : String) : String | ::Nil\n # A field's lines apart, in the order they came: `header` joins them\n # with \", \", and `Set-Cookie` is the field that join breaks (RFC 9110\n # §5.3), since a cookie's `Expires` date has a comma in it - two cookies\n # read as \"a=1; Expires=Wed, 21 Oct 2026 07:28:00 GMT, b=2\", which no\n # split takes back apart. Empty for a field that is not there.\n def header_values(name : String) : Array(String)\n def headers : Hash(String, String)\n # A response a client read: its body comes from *reader*, and *url* is\n # where it came from.\n def initialize(status : Int32, reason : String, headers : Hash(String, String), apart : Hash(String, Array(String)) | ::Nil, reader : BodyReader, url : String, decoded : Bool)\n def initialize(status : Int32, reason : String, headers : Hash(String, String), body : String, apart : Hash(String, Array(String)) | ::Nil = nil)\n # A response to write: the reason is the status's own, the headers the\n # caller's. `Response.new(200, \"hello\")` is the common shape.\n def initialize(status : Int32, body : String = \"\", headers : Hash(String, String) = {} of String => String)\n # A response whose body is *source*, copied out by the server and\n # closed after.\n def initialize(status : Int32, source : IyiIO, headers : Hash(String, String) = {} of String => String)\n # The body read whole into the response, which is answered; why not,\n # when it cannot be.\n def load : Response | Cancelled | SocketError | HTTPError\n def reason : String\n def source : IyiIO | ::Nil\n def status : Int32\n # A response whose body the block writes as it goes, chunked.\n def self.stream(status : Int32, headers : Hash(String, String) = {} of String => String, &writer : (BodyWriter -> Nil)) : Response\n def success? : Bool\n def takeover : Proc(IyiSocket | TlsStream, String, Nil) | ::Nil\n protected def takeover=(takeover : Proc(IyiSocket | TlsStream, String, Nil)) : Proc(IyiSocket | TlsStream, String, Nil)\n def tls_resumed=(resumed : Bool) : Bool\n # Whether the `https://` connection the answer came over resumed a TLS\n # session by a ticket an `HTTPClient` kept, rather than a full\n # handshake; false in the clear and for one a program made.\n def tls_resumed? : Bool\n # A 101 Switching Protocols with *headers* (`Upgrade` and the\n # protocol's own), after which the connection is the block's: it is\n # handed the connection - the socket, or under `serve(tls:)` the\n # `TlsStream` over it - and the bytes the client sent after its request,\n # and the connection closes when it returns.\n def self.upgrade(headers : Hash(String, String), &takeover : (IyiSocket | TlsStream, String -> Nil)) : Response\n # The URL the answer came from, after any redirects; nil for one a\n # program made.\n def url : String | ::Nil\n def writer : Proc(BodyWriter, Nil) | ::Nil\n protected def writer=(writer : Proc(BodyWriter, Nil)) : Proc(BodyWriter, Nil)\nend\n\npub struct Server\n # The headers a coded body goes with: the coding, and `Vary` so that a\n # cache keeps the answers apart. A `Content-Length` the block gave is\n # the plain body's, and goes.\n protected def self.coded(response : Response, coding : String) : Response\n # The coding *response* goes in to the client of *req*, or nil: one the\n # client takes (RFC 9110 §12.5.3, a `q` of 0 refusing it), for an\n # answer with a body and a type worth compressing, and no coding of its\n # own. A whole body goes `zstd` when the client's `q` for it is at least\n # its `q` for gzip, and `gzip` otherwise. The tie goes to zstd for the\n # server's time: measured on this machine, release builds, on\n # src/std/http.iyi (199,134 bytes) `Gzip.compress` wrote 54,484 bytes in\n # 5 ms and `Zstd.compress` 61,753 in 1 ms, and on CHANGELOG.md (1.5 MB)\n # 552,446 in 58 ms against 589,785 in 13 ms - a quarter of the time, for\n # an eighth more bytes at most. A stream - an `IyiIO` or a\n # `Response.stream` - goes gzip or as it is: std/compress's `Deflater`\n # writes each piece as it comes, where std/zstd's compressor takes its\n # input whole, and a frame a write would code each piece alone.\n def self.coding_for(req : Request, response : Response) : String | ::Nil\n # A string body compressed whole in *coding*, `zstd` or `gzip`, with\n # the headers that say so. `Zstd.compress`'s frame needs a window of\n # 1 MiB at most, inside the 8 MB RFC 9659 §3 holds an encoder to.\n protected def self.compressed(response : Response, coding : String) : Response\n # A response's head. `Content-Length` is *length*'s - the caller's own\n # line is checked against it, and kept only where no body follows it: a\n # HEAD, or a 304 that names the length a 200 would have (RFC 9110 §8.6) -\n # or `Transfer-Encoding: chunked` when *chunked*, or neither for a body\n # the connection's close ends. `Connection` says what the connection will\n # do next, except on an *upgrade*, whose own line says it. A 1xx, 204 or\n # 304 has no content (RFC 9110 §15), and writes neither a body nor a\n # length of its own (§8.6): the body was read by the client as the start\n # of the next answer, and `Content-Length: 0` on a 304 told a cache that\n # the resource had become empty. A field added on lines of its own\n # (`add_header`) is written a line each.\n def self.format_head(response : Response, head : Bool, close : Bool, length : Int64 | ::Nil, chunked : Bool, upgrade : Bool = false) : String\n # The response as it goes on the wire, whole: `format_head` for its\n # string body, and the body.\n def self.format_response(response : Response, head : Bool, close : Bool) : String\n # One connection: requests until the client is done. A request that is\n # not HTTP is answered with why and the connection closed, since the\n # bytes after it cannot be trusted to be a request either. Requests that\n # came in one read are read where they lie, one after another. A\n # connection that opens with HTTP/2's preface is served as HTTP/2. Under\n # *tls* the handshake comes first, and the connection ends with\n # close_notify. A connection this side wrote last on - an answer, a\n # refusal, a GOAWAY - closes by lingering (`Conn#close`), so the reset\n # a close with the client's bytes unread is cannot take what was\n # written from the client before it reads it. One that timed out or was\n # cancelled, with nothing of this side's waiting to be read, closes at\n # once, and so does one whose client said `close` and sent nothing past\n # its request (`serve_h1`).\n def self.handle(client : IyiSocket, handler : Proc(Request, Response), listener : IyiSocket | ServeStop | Nil = nil, idle : Hash(UInt64, Conn) | ::Nil = nil, stream_bodies : Bool = false, compress : Bool = false, timeout_ms : Int32 = 0, tls : TlsServerConfig | TlsFrozenServerConfig | Nil = nil, request_timeout_ms : Int32 = 0) : Nil\n def initialize\n # A request's head - the text before its blank line - read and checked,\n # or why it is not one.\n def self.parse_head(text : String) : RequestHead | HTTPError\n # A request at *from* in `text`, if a whole one is there. No request and\n # no error means \"not yet\". `consumed` counts from the start of `text`.\n def self.parse_request(text : String, from : Int32 = 0) : ParsedRequest\n # Serve `listener` until it is closed, on *workers* threads: each\n # connection is a task on the pool `spawn(unit: :new)` starts (SPEC.md\n # III.4.13), started by whichever worker is free and served there to\n # its end, while the thread that called this accepts. The block runs on\n # the connection's worker, once per request, on several workers at\n # once; what it answers is written back.\n # The close ends a connection waiting on its client too, for its next\n # request or for the rest of one, and one that is answering ends when its\n # answer is written: a keep-alive client that sat idle kept `serve` from\n # returning for as long as it stayed, and so did one that had sent\n # what has arrived of a request so far - one byte of it was enough.\n #\n # *stream_bodies*: the block reads each request's body from `body_io`\n # rather than being handed it whole. *compress*: an answer whose type\n # std/mime calls compressible goes zstd or gzip to a client that\n # accepts one (`coding_for`).\n #\n # *timeout_ms*: a request's head must have arrived within it, counted\n # from when the server starts waiting for one - a kept-alive connection's\n # idle time included - and no read of a body nor write of an answer may\n # wait longer; a connection that does is closed. Without it, a client\n # that sends a byte of its head every few seconds (Slowloris) holds a\n # connection and its fiber for as long as it likes, and so does one that\n # reads none of its answer. A minute by default, nginx's\n # `client_header_timeout`; 0 for none. A connection handed over by\n # `Response.upgrade` waits as its protocol likes.\n #\n # *request_timeout_ms*: the whole request - head and body - must have\n # arrived within it, counted from the same moment as *timeout_ms*, as\n # Go's `ReadTimeout` beside its `ReadHeaderTimeout`; the head is held to\n # the sooner of the two. Without it a body sent a byte every few seconds\n # was bounded per read alone, and held its connection for as long as it\n # dripped. A block that reads the body itself (*stream_bodies*) reads\n # under it until the block returns, and what it leaves unread is read\n # past under it too. Over HTTP/2 the wait for each request and its body\n # is held to it on the connection the same way, the head to the sooner\n # of the two. Five minutes by default, which a 64 MiB body (`MAX_BODY`)\n # meets at 224 KB/s; a server taking large uploads over slow links\n # raises it. 0 for none. A connection past it is closed, unanswered, as\n # one past *timeout_ms* is.\n #\n # *max_connections*: at most this many connections are served at once;\n # 1024 by default, 0 for no limit. Past it the server stops accepting\n # until one ends, so the next connection waits in the kernel's listen\n # queue - queued, not refused, as Go's `netutil.LimitListener` does - and\n # is served in its turn; a client that gives up waiting goes unserved.\n # Each served connection holds its place until it closes, a kept-alive\n # one between requests too, which *timeout_ms* bounds. The process's\n # own limit on descriptors may come first.\n #\n # *tls*: each connection is TLS (std/tls's `TlsServer`, 1.3 or 1.2) under this\n # configuration, its handshake held to *timeout_ms* too and run on the\n # worker that serves it; one whose handshake fails is closed, the\n # client told why by an alert. Inside it a connection that opens with\n # HTTP/2's preface - as a client whose ALPN chose `h2` does, when\n # `tls.alpn` offers it - is served HTTP/2, and any other HTTP/1.1. A\n # `Response.upgrade` block over TLS is handed the `TlsStream`, so a\n # WebSocket is `wss://`. On more than one worker the config is a\n # `TlsFrozenServerConfig` - `config.frozen` - since every worker reads\n # it at once: its ticket keys are one set behind a lock, so a ticket a\n # connection on one worker was sent resumes on any other, and an\n # identity source given to `frozen` is a block the compiler checked. A\n # `TlsServerConfig` itself is refused at the call (\"Server.serve hands\n # `tls : TlsServerConfig` to other threads, several at once, and it is\n # not Share: ... Pass `config.frozen` ...\"), not frozen here: `frozen`\n # is a copy, so a property assigned after the server started would be\n # served by no worker and nothing would say so, and a config with an\n # `identity_source` cannot be frozen without its source given again as\n # a block the compiler checks. Under `workers: 1` either is served.\n #\n # *workers*: the pool's size, `IyiWorkers.count` by default - `IYI_WORKERS`\n # from the environment, else the cores this process may run on. The\n # pool is the process's one, and its size does not change once it has\n # started, so a count other than the one it started with panics. Every\n # worker runs the block at once, so the compiler checks it where it is\n # written as it checks a parallel task's (`@[Share]`, SPEC.md III.4.4):\n # every value it captures must be `Share` - a table behind a `Mutex`\n # (std/mutex), a count in an `Atomic`, a `SharedRouter` (std/router) -\n # every captured local assigned once, and the constants and class\n # variables it names `Share`. The refusal names the variable, why its\n # type is not `Share`, and the two ways out: the lock, or `workers: 1`.\n # `workers: 1`, written as the literal, serves on the calling thread\n # alone - each connection a task beside the accept loop, as `spawn`\n # makes one - and its block is not checked: a block that calls a\n # `Router`, appends to an `Array` or keeps a handle that is not `Share`\n # is served there. A count in a variable is checked whatever it holds,\n # since the compiler cannot know it is 1. A count of 1 from the default\n # (`IYI_WORKERS=1`, one core) serves on the calling thread too.\n #\n # A connection stays on the worker that started it (III.4.13, part 1):\n # connections spread over the workers free as they arrive, and one kept\n # alive is not moved later. A block that computes without parking holds\n # its worker meanwhile, as any task does. The listener is the calling\n # thread's, and a close of it is made there: a block that stops the\n # server sends on a `Channel` a task beside the server receives from,\n # and that task closes it. At the stop each connection waiting on its\n # client is closed on its own worker, sent close_notify there first\n # under *tls*.\n def self.serve(listener : IyiSocket, stream_bodies : Bool = false, compress : Bool = false, timeout_ms : Int32 = 60000, tls : C = nil, request_timeout_ms : Int32 = REQUEST_TIMEOUT, max_connections : Int32 = MAX_CONNECTIONS, workers : Int32 = IyiWorkers.count, &handler : (Request -> Response)) : Nil forall C\n # `serve` with a value for its handler rather than a block: a\n # `SharedProc`, a `SharedRouter` (std/router), or one of the program's\n # own, whose `call(request : Request) : Response` answers each request,\n # as Go's `http.Handler` does. On more than one worker it must be\n # `Share` (SPEC.md III.4.4), since every connection on every worker\n # calls the one value at once: the compiler refuses one that is not at\n # the argument, naming the field that failed and the two ways out - a\n # count in an `Atomic`, a pool or a cache behind a `Mutex`, a\n # `SharedRouter` for a `Router`, or `workers: 1`, under which any value\n # with that `call` is served on the calling thread, a `Router`\n # included. What the check does not see is the edge `spawn(unit: :new)`\n # has too: a method the handler calls that writes a module's state. A\n # struct handler is copied into each connection's task, and an `Atomic`\n # in it with it: what the connections share is a class, or held by\n # one.\n #\n # The rest is the block form's: *stream_bodies*, *compress*,\n # *timeout_ms*, *tls* (`config.frozen` on more than one worker),\n # *request_timeout_ms*, *max_connections* and *workers*, HTTP/2 by its\n # preface, `Response.upgrade`, and a close of *listener* - on the\n # thread that called this - ending every connection waiting on its\n # client and returning once the others have answered.\n def self.serve(listener : IyiSocket, handler : H, stream_bodies : Bool = false, compress : Bool = false, timeout_ms : Int32 = 60000, tls : C = nil, request_timeout_ms : Int32 = REQUEST_TIMEOUT, max_connections : Int32 = MAX_CONNECTIONS, workers : Int32 = IyiWorkers.count) : Nil forall H, C\n protected def self.writer_for(socket : IyiSocket | TlsStream, chunked : Bool, gzip : Bool) : BodyWriter\nend\n\n# A TLS connection used as a socket is: read into a buffer, written to,\n# closed, its timeouts set - by a `Conn`, a `BodyWriter`, and a connection\n# handed over by an upgrade (`wss://` at either end). What TLS refuses is a\n# `SocketError` carrying TLS's own sentence (`layer_reason`), so answers\n# keep the types they have over a plain socket.\npub class TlsStream\n # The socket closed at once, nothing sent or read first.\n def abort : Nil\n # close_notify, the sending half shut, and what the peer still sends\n # read past for a bounded time before the socket closes\n # (`TlsConnection#close`).\n def close : Nil\n # close_notify and the sending half shut; reads go on until the peer's\n # own (`TlsConnection#close_write`).\n def close_write : SocketError | ::Nil\n def closed? : Bool\n def initialize(tls : TlsConnection, host : String)\n # The peer's certificate chain, leaf first, as the handshake checked it:\n # the server's, at a client; the client's, at a server that asked.\n def peer_certificates : Array(Certificate)\n # Up to *count* bytes into *buffer*: 0 once the peer closed with\n # close_notify.\n def read(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n # Up to *max_bytes* as a string, what is held first: \"\" once the peer\n # closed with close_notify. `IyiSocket#read`'s form, for a block an\n # upgrade hands either.\n def read(max_bytes : Int32 = 4096) : String | Cancelled | SocketError\n # How long a read or a write of the connection under it may wait.\n def read_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\n # Whether the handshake resumed a session (`TlsConnection#resumed?`).\n def resumed? : Bool\n def write(text : String) : Int32 | Cancelled | SocketError\n def write_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\nend\n\n# What `HTTP.upgrade` answers: the 101, and the connection, which is the\n# caller's now - the socket, and the bytes the server sent after its head.\npub class Upgraded\n def initialize(response : Response, socket : IyiSocket | TlsStream, leftover : String)\n def leftover : String\n def response : Response\n # The connection: the socket, or for `https://` the `TlsStream` over it.\n def socket : IyiSocket | TlsStream\nend\n\nimpl Error for HTTPError\n def message : String\nend\n" }, { "name": "std/http_proxy", @@ -286,31 +286,31 @@ { "name": "std/idna", "path": "src/std/idna.iyi", - "sourceSha256": "d4e0bdb1a38a20a56ef6638b39f6533347dec2b8f8bd30fb4a747256b654d693", + "sourceSha256": "1d67b14b54bc26e16b728e64a0ec946351cfcf9b16eab3b7f456cb0fd7030a1a", "surface": "module std/idna\n\n# Host names to and from the ASCII form DNS looks up, IDNA2008's way, label\n# by label; the header says what is checked and what is not.\npub struct IDNA\n def initialize\n # RFC 5892's derived property of *char* (section 3), computed in the\n # order the RFC gives: the exceptions (section 2.6), unassigned code\n # points, the LDH characters, the joiners, and then a letter, digit or\n # mark that neither case folding nor NFKC changes, that is not default\n # ignorable, white space or a noncharacter, and not in the ignorable\n # blocks or old Hangul jamo. Everything else is Disallowed.\n def self.property(char : Char) : IdnaProperty\n # The name as DNS looks it up: every U-label as its `xn--` A-label,\n # every ASCII label lower-cased.\n def self.to_ascii(name : String) : String | IdnaError\n # The name as a person reads it: every A-label decoded, every ASCII\n # label lower-cased; checked as `to_ascii` checks it.\n def self.to_unicode(name : String) : String | IdnaError\n # `U+00FC`: four hex digits at least, upper case.\n def self.u_plus(cp : Int64) : String\nend\n\n# What `IDNA.to_ascii`, `IDNA.to_unicode` and `URI.ascii_host` answer for a\n# name they cannot carry: the sentence names the label and why.\npub struct IdnaError\n def initialize(message : String)\n def to_s : String\nend\n\n# The derived property of RFC 5892 section 2: whether a code point may\n# appear in a U-label (PValid), only in a context (ContextJ, ContextO), not\n# at all (Disallowed), or is unassigned in the Unicode `std/unicode` holds.\npub enum IdnaProperty : Int32\n PValid = 0\n ContextJ = 1\n ContextO = 2\n Disallowed = 3\n Unassigned = 4\nend\n\n# RFC 3492's bootstring with its Punycode parameters (section 5).\npub struct Punycode\n # The string *text* is the Punycode of (section 6.2), or the reason it\n # is none. Digits are read in either case; basic code points are kept\n # in the case they were written.\n def self.decode(text : String) : String | PunycodeError\n # The Punycode of *text*'s code points (section 6.3). Total: every\n # string has one. Bytes that are not UTF-8 are read as `each_char` reads\n # them, U+FFFD each.\n def self.encode(text : String) : String\n def initialize\nend\n\n# What `Punycode.decode` answers for input that is not Punycode.\npub struct PunycodeError\n def initialize(message : String)\n def to_s : String\nend\n\nimpl Error for PunycodeError\n def message : String\nend\n\nimpl Error for IdnaError\n def message : String\nend\n" }, { "name": "std/indexable", "path": "src/std/indexable.iyi", - "sourceSha256": "150f44ac52d880972ddb618cffee1029ef10f3a5f562a472e6986735bb2bfb9d", + "sourceSha256": "b2842e5d053bf0356ee1019e9aaba651a7c18b2b4cc5efb1c9fd167a728f78cc", "surface": "module std/indexable\n\npub trait Indexable\n type Elem\n # Returns the element at the given index. Negative indices count from the\n # end, so `a[-1]` returns the last element. Raises when index is out of\n # bounds, naming the index as it was written and the size there was:\n # `index out of range` on its own told a caller with a computed index\n # nothing it did not already know. The wording is `Array`'s\n # (src/iyi/array.iyi), because one mistake gets one sentence.\n def [](index : Int32) : Elem\n # Returns the element at the given index, or nil when index is out of bounds.\n # Negative indices count from the end.\n def []?(index : Int32) : Elem | ::Nil\n # Binary search on a sorted collection. Returns the first element for which\n # the block returns true, or nil if no element matches.\n def bsearch(& : (Elem -> Bool)) : Elem | ::Nil\n # Binary search returning the index of the first element for which the block\n # returns true, or nil.\n def bsearch_index(& : (Elem -> Bool)) : Int32 | ::Nil\n # Single-index navigation.\n def dig(index : Int32) : Elem\n # Single-index nilable navigation.\n def dig?(index : Int32) : Elem | ::Nil\n # Default iteration yielding elements sequentially from 0 up to size - 1.\n # It answers `Enumerable`'s requirement for a type that also says `impl\n # Enumerable`, which is one more line than the ergonomics claim implies\n # (SPEC.md II.6) — the claim that this trait supplied it was false, and\n # `Array` went without `zip`, `take` and the rest until the line existed.\n def each(& : (Elem -> Nil)) : Nil\n # Yields consecutive overlapping windows of n elements each.\n def each_cons(n : Int32, & : (Array(Elem) -> Nil)) : Nil\n # Returns an Array of consecutive overlapping windows of n elements each.\n def each_cons(n : Int32) : Array(Array(Elem))\n # Yields each valid index from 0 up to size - 1.\n def each_index(& : (Int32 -> Nil)) : Nil\n # Yields slices of n elements each to the given block.\n def each_slice(n : Int32, & : (Array(Elem) -> Nil)) : Nil\n # Returns an Array of slices of n elements each.\n def each_slice(n : Int32) : Array(Array(Elem))\n # Returns true when the collection contains no elements.\n def empty? : Bool\n # Structural equality comparing collection size and element values.\n def equals?(other : self) : Bool\n # Structural equality comparing elements with a custom predicate block.\n def equals?(other : self, & : (Elem, Elem -> Bool)) : Bool\n # Returns the element at index. If out of bounds, returns the default value.\n def fetch(index : Int32, default : Elem) : Elem\n # Returns the element at index. If out of bounds, yields the original index\n # to the given block and returns its result.\n def fetch(index : Int32, & : (Int32 -> Elem)) : Elem\n # Returns the first element. Raises when the collection is empty.\n def first : Elem\n # Returns the first element, or nil when the collection is empty.\n def first? : Elem | ::Nil\n # Combines the elements' hashes. Wrapping arithmetic, because a hash is a\n # number modulo the word and nothing else: written with the checked `*` and\n # `+`, this panicked with \"arithmetic overflow\" the moment a collection was\n # long enough for `31 * h` to leave `Int32` - six elements of two digits\n # each was enough, while the five-element cases in the gates passed.\n #\n # Mixed as the prelude's `Array#hash` is, which is also what keeps an\n # array's and another indexable's hashes one function: `31 * h + e` gave\n # a grid of pairs a few hashes between many, and a table probing a slot\n # at a time merged their runs.\n def hash : Int32\n # Returns the first index where element equals value, or nil if not found.\n def index(value : Elem) : Int32 | ::Nil\n # Returns the first index for which the block returns true, or nil.\n def index(& : (Elem -> Bool)) : Int32 | ::Nil\n # Joins element string representations with the specified separator.\n #\n # Into one builder, as `Array#join` writes: adding each element to the\n # text so far copied that text once per element, and 20,000 elements\n # took 1.8 s where the same array's `join` took 1 ms.\n def join(separator : String = \"\") : String\n # Returns the last element. Raises when the collection is empty.\n def last : Elem\n # Returns the last element, or nil when the collection is empty.\n def last? : Elem | ::Nil\n # Yields elements in reverse order, from size - 1 down to 0.\n def reverse_each(& : (Elem -> Nil)) : Nil\n # Returns the last index where element equals value, or nil if not found.\n def rindex(value : Elem) : Int32 | ::Nil\n # Returns the last index for which the block returns true, or nil.\n def rindex(& : (Elem -> Bool)) : Int32 | ::Nil\n # One element, or *n* distinct ones, chosen by `random`, a fresh generator\n # when none is given, the way std/random's `Array#sample` chooses them:\n # so a `Deque` makes the other library's choices in its order, as an\n # array does. `sample(Int32)` was a seed here and a count there, so\n # `[10, 20, 30, 40, 50].sample(3)` answered 20 without std/random and\n # three elements with it, a `Deque`'s answered 20 either way, and\n # `sample` answered the middle element on every run.\n def sample(random : Random | ::Nil = nil) : Elem\n def sample(n : Int32, random : Random | ::Nil = nil) : Array(Elem)\n # Returns a sample element using the index returned by the caller's block.\n def sample(& : (Int32 -> Int32)) : Elem\n abstract def size : Int32\n # Copies elements into a newly allocated Array with pre-allocated capacity.\n def to_a : Array(Elem)\n abstract def unsafe_fetch(index : Int32) : Elem\n # Returns an Array containing elements at the given indices array.\n def values_at(indexes : Array(Int32)) : Array(Elem)\n # Returns an Array containing elements at the given indices.\n def values_at(*indexes : Int32) : Array(Elem)\nend\n\n# `Indexable` with one more requirement, `unsafe_put`, from which the\n# in-place operations follow. In-place variants are spelled `_in_place`;\n# the copies (`rotated`, `reversed`) live on the concrete type, since a\n# trait cannot build one without knowing how.\npub trait IndexableMutable : ::Std::Indexable::Indexable\n type Elem\n # The count elements from offset become value. A negative count is a\n # mistake; a window that runs past the end is refused by naming the\n # index that would have been written.\n def fill(value : Elem, offset : Int32, count : Int32) : self\n # Every element becomes value.\n def fill(value : Elem) : self\n # Every element becomes what the block answers for its index.\n def fill(& : (Int32 -> Elem)) : self\n # Reverses elements in place.\n def reverse_in_place : self\n # Rotates elements in place so that the element at position n comes\n # first; a negative n rotates the other way. Three reversals, no\n # scratch space.\n def rotate_in_place(n : Int32 = 1) : self\n # Swaps two elements in place.\n def swap(index0 : Int32, index1 : Int32) : self\n abstract def unsafe_put(index : Int32, value : Elem) : Elem\n # Replaces the element at index with what the block answers for it,\n # and answers the new value.\n def update(index : Int32, & : (Elem -> Elem)) : Elem\nend\n\nimpl Indexable for Array(T) forall T\n def unsafe_fetch(index : Int32) : T\nend\n\nimpl IndexableMutable for Array(T) forall T\n def unsafe_put(index : Int32, value : T) : T\nend\n\nclass ::Array(T)\n # Every pair with an element of the other array, this array's order\n # first. Not `product`: on an `Enumerable`, `product` is the\n # multiplicative fold, and one name means one thing.\n def cartesian(other : Array(U)) : Array(Tuple(T, U)) forall U\n # Forgets every element.\n def clear : Array(T)\n # Removes the nils in place.\n def compact_in_place : Array(T)\n # Removes and answers the element at index, shifting what follows left.\n def delete_at(index : Int32) : T\n # Removes every element the block answers true for, in place, keeping\n # the order of the rest; one pass, no per-element shifting.\n def delete_if(& : (T -> Bool)) : Array(T)\n # The same pairs, handed to a block in the same order, with no array of\n # them made - the other library's `each_cartesian`.\n def each_cartesian(other : Array(U), & : (T, U -> _)) : Nil forall U\n # Inserts value at index, shifting what follows right. An index of\n # `size` appends; `-1` also appends, as the position after the last.\n def insert(index : Int32, value : T) : Array(T)\n # Keeps only the elements the block answers true for, in place.\n def keep_if(& : (T -> Bool)) : Array(T)\n # A copy rotated so that the element at position n comes first; the\n # past participle names the copy, as `sorted` and `reversed` do, and\n # `rotate_in_place` is the one that moves this array.\n def rotated(n : Int32 = 1) : Array(T)\nend\n" }, { "name": "std/ini", "path": "src/std/ini.iyi", - "sourceSha256": "95cf42e3a57ffd9864fd1692bdcdbad9003b3ac1f0a75e4da0dad940b8b6fa03", + "sourceSha256": "6e4d743f1eb583c7c28a5f2ae49b293af9ca24bd21725727f8e71a6428067d52", "surface": "module std/ini\n\npub struct INI\n def self.build(ini : Hash(String, Hash(String, String)), space : Bool = false) : String\n def self.build(io : IO, ini : Hash(String, Hash(String, String)), space : Bool = false) : Nil\n def initialize\n # Past a byte order mark, which Windows PowerShell 5.1's `Set-Content\n # -Encoding UTF8` and older Notepad write: it panicked \"expected\n # declaration at line 1, column 8\" at a `[section]` behind one, and the\n # first key of a file without a section kept it and never matched.\n def self.parse(string : String) : Hash(String, Hash(String, String))\n def self.parse(io : IO) : Hash(String, Hash(String, String))\nend\n" }, { "name": "std/int", "path": "src/std/int.iyi", - "sourceSha256": "ac5815541443b344d8f4a81fe4786fd5402d92743b6d31a77fe6de256b476fcf", + "sourceSha256": "2aae1b4566101aa34d4e837711e75d73682c18e6fd7f279e3a076538635b1fbe", "surface": "module std/int\n\nstruct ::Float64\n def !=(other : Int8) : Bool\n def !=(other : Int16) : Bool\n def !=(other : Int128) : Bool\n def !=(other : UInt16) : Bool\n def !=(other : UInt32) : Bool\n def !=(other : UInt128) : Bool\n def *(other : Int8) : self\n def *(other : Int16) : self\n def *(other : Int128) : self\n def *(other : UInt16) : self\n def *(other : UInt32) : self\n def *(other : UInt128) : self\n def +(other : Int8) : self\n def +(other : Int16) : self\n def +(other : Int128) : self\n def +(other : UInt16) : self\n def +(other : UInt32) : self\n def +(other : UInt128) : self\n def -(other : Int8) : self\n def -(other : Int16) : self\n def -(other : Int128) : self\n def -(other : UInt16) : self\n def -(other : UInt32) : self\n def -(other : UInt128) : self\n def <(other : Int8) : Bool\n def <(other : Int16) : Bool\n def <(other : Int128) : Bool\n def <(other : UInt16) : Bool\n def <(other : UInt32) : Bool\n def <(other : UInt128) : Bool\n def <=(other : Int8) : Bool\n def <=(other : Int16) : Bool\n def <=(other : Int128) : Bool\n def <=(other : UInt16) : Bool\n def <=(other : UInt32) : Bool\n def <=(other : UInt128) : Bool\n # The integer's comparison, exact, with the sides swapped.\n def ==(other : Int8) : Bool\n # The integer's comparison, exact, with the sides swapped.\n def ==(other : Int16) : Bool\n # The integer's comparison, exact, with the sides swapped.\n def ==(other : Int128) : Bool\n # The integer's comparison, exact, with the sides swapped.\n def ==(other : UInt16) : Bool\n # The integer's comparison, exact, with the sides swapped.\n def ==(other : UInt32) : Bool\n # The integer's comparison, exact, with the sides swapped.\n def ==(other : UInt128) : Bool\n def >(other : Int8) : Bool\n def >(other : Int16) : Bool\n def >(other : Int128) : Bool\n def >(other : UInt16) : Bool\n def >(other : UInt32) : Bool\n def >(other : UInt128) : Bool\n def >=(other : Int8) : Bool\n def >=(other : Int16) : Bool\n def >=(other : Int128) : Bool\n def >=(other : UInt16) : Bool\n def >=(other : UInt32) : Bool\n def >=(other : UInt128) : Bool\n def fdiv(other : Int8) : self\n def fdiv(other : Int16) : self\n def fdiv(other : Int128) : self\n def fdiv(other : UInt16) : self\n def fdiv(other : UInt32) : self\n def fdiv(other : UInt128) : self\n def to_i128 : Int128\n def to_i16 : Int16\n def to_i8 : Int8\n def to_u : UInt32\n def to_u128 : UInt128\n def to_u16 : UInt16\n def to_u32 : UInt32\n # Not the instruction: a double to a 128-bit integer is a call\n # into compiler-rt (`__fixdfti`) on aarch64, which the floor\n # (SPEC.md III.9) does not link. The mantissa and exponent are\n # read off the bits and shifted into place instead; the caller\n # has already refused what does not fit.\n def unsafe_to_i128 : Int128\n # The prelude declares the unchecked form for its own five, as it\n # declares the checked one, so declaring it here too would be R-3's\n # duplicate.\n def unsafe_to_i16 : Int16\n # The conversions to the integers the prelude does not reach. The\n # unchecked form saturates, as the prelude's does - past the range it is\n # the nearest bound and NaN is 0, in every build and on every target -\n # but for the two 128-bit ones below; the checked form truncates first\n # and refuses what the truncation does not hold:\n # `127.9.to_i8` is 127 and `(-0.5).to_u16` is 0, where the range was\n # once checked on the untruncated value and both were refused. NaN and\n # the infinities are refused. The prelude's own five (`to_i`, `to_i32`,\n # `to_i64`, `to_u8`, `to_u64`) are the compiler's and say the same.\n # The prelude declares the unchecked form for its own five, as it\n # declares the checked one, so declaring it here too would be R-3's\n # duplicate.\n def unsafe_to_i8 : Int8\n # The prelude declares the unchecked form for its own five, as it\n # declares the checked one, so declaring it here too would be R-3's\n # duplicate.\n def unsafe_to_u : UInt32\n # Not the instruction: a double to a 128-bit integer is a call\n # into compiler-rt (`__fixdfti`) on aarch64, which the floor\n # (SPEC.md III.9) does not link. The mantissa and exponent are\n # read off the bits and shifted into place instead; the caller\n # has already refused what does not fit.\n def unsafe_to_u128 : UInt128\n # The prelude declares the unchecked form for its own five, as it\n # declares the checked one, so declaring it here too would be R-3's\n # duplicate.\n def unsafe_to_u16 : UInt16\n # The prelude declares the unchecked form for its own five, as it\n # declares the checked one, so declaring it here too would be R-3's\n # duplicate.\n def unsafe_to_u32 : UInt32\nend\n\nstruct ::Int\n # Against any integer, as the other library's `Int#<=>` is, the widths\n # compared exactly by the primitives above. Against `self` only, an\n # `Int64` beside an `Int32` did not type, and once `std/traits` added\n # `Int32#<=>(Int32)` the literal in `1 <=> 1_i64` cast to that and to\n # `Float64` both: \"ambiguous call\" where the other library answers 0.\n def <=>(other : ::Int) : Int32\n # Bit `n`, 0 or 1; a bit past the width is 0 (or the sign).\n def bit(n : Int32) : Int32\n def bits_set?(mask : self) : Bool\n # The character with this code point: the prelude's `Int32#chr` contract\n # for every integer type, every code point but the surrogates. It stopped\n # at ASCII after the prelude's had moved on, so `233.chr` was 'é' and\n # `233_i64.chr` panicked \"233 is out of char range\".\n def chr : Char\n # The digits, least significant first: `1234.digits` is `[4, 3, 2, 1]`.\n # A negative number has no digits in this sense and is refused.\n def digits(base : Int32 = 10) : Array(Int32)\n # Euclid on the side of zero where every magnitude fits, as `Math.gcd`\n # works: the non-positive side for a signed type, its minimum included.\n # Taking `abs` first refused the minimum, and `(-2147483648).gcd(6)`\n # panicked \"arithmetic overflow\" where `Math.gcd` answered 2. Only\n # gcd(minimum, 0), whose answer does not fit, overflows.\n def gcd(other : self) : self\n def lcm(other : self) : self\nend\n\nstruct ::Int128\n def !=(other : Int8) : Bool\n def !=(other : Int16) : Bool\n def !=(other : Int32) : Bool\n def !=(other : Int64) : Bool\n def !=(other : Int128) : Bool\n def !=(other : UInt8) : Bool\n def !=(other : UInt16) : Bool\n def !=(other : UInt32) : Bool\n def !=(other : UInt64) : Bool\n def !=(other : UInt128) : Bool\n def !=(other : Float64) : Bool\n def %(other : Int128) : Int128\n def &(other : Int8) : self\n def &(other : Int16) : self\n def &(other : Int32) : self\n def &(other : Int64) : self\n def &(other : Int128) : self\n def &(other : UInt8) : self\n def &(other : UInt16) : self\n def &(other : UInt32) : self\n def &(other : UInt64) : self\n def &(other : UInt128) : self\n def &*(other : Int8) : self\n def &*(other : Int16) : self\n def &*(other : Int32) : self\n def &*(other : Int64) : self\n def &*(other : Int128) : self\n def &*(other : UInt8) : self\n def &*(other : UInt16) : self\n def &*(other : UInt32) : self\n def &*(other : UInt64) : self\n def &*(other : UInt128) : self\n def &+(other : Int8) : self\n def &+(other : Int16) : self\n def &+(other : Int32) : self\n def &+(other : Int64) : self\n def &+(other : Int128) : self\n def &+(other : UInt8) : self\n def &+(other : UInt16) : self\n def &+(other : UInt32) : self\n def &+(other : UInt64) : self\n def &+(other : UInt128) : self\n def &-(other : Int8) : self\n def &-(other : Int16) : self\n def &-(other : Int32) : self\n def &-(other : Int64) : self\n def &-(other : Int128) : self\n def &-(other : UInt8) : self\n def &-(other : UInt16) : self\n def &-(other : UInt32) : self\n def &-(other : UInt64) : self\n def &-(other : UInt128) : self\n def *(other : Int8) : self\n def *(other : Int16) : self\n def *(other : Int32) : self\n def *(other : Int64) : self\n def *(other : Int128) : self\n def *(other : UInt8) : self\n def *(other : UInt16) : self\n def *(other : UInt32) : self\n def *(other : UInt64) : self\n def *(other : UInt128) : self\n def *(other : Float64) : Float64\n def +(other : Int8) : self\n def +(other : Int16) : self\n def +(other : Int32) : self\n def +(other : Int64) : self\n def +(other : Int128) : self\n def +(other : UInt8) : self\n def +(other : UInt16) : self\n def +(other : UInt32) : self\n def +(other : UInt64) : self\n def +(other : UInt128) : self\n def +(other : Float64) : Float64\n def -(other : Int8) : self\n def -(other : Int16) : self\n def -(other : Int32) : self\n def -(other : Int64) : self\n def -(other : Int128) : self\n def -(other : UInt8) : self\n def -(other : UInt16) : self\n def -(other : UInt32) : self\n def -(other : UInt64) : self\n def -(other : UInt128) : self\n def -(other : Float64) : Float64\n def - : Int128\n # An integer divided by a float is a float, as the prelude has it for\n # `Int32` and `Int64` - and for `UInt8` and `UInt64` too, which the\n # prelude does not write: `x / 3.0` did not type for them, and with a\n # literal divisor the `Float32` overload took it, so `1_u8 / 3.0` was a\n # single, 0.33333334.\n def /(other : Float64) : Float64\n # The zero is checked here rather than left to the processor, which\n # reports an integer division by zero as a floating-point fault.\n def //(other : Int128) : Int128\n def <(other : Int8) : Bool\n def <(other : Int16) : Bool\n def <(other : Int32) : Bool\n def <(other : Int64) : Bool\n def <(other : Int128) : Bool\n def <(other : UInt8) : Bool\n def <(other : UInt16) : Bool\n def <(other : UInt32) : Bool\n def <(other : UInt64) : Bool\n def <(other : UInt128) : Bool\n def <(other : Float64) : Bool\n # A shift by the width or more answers zero (or the sign, shifting a\n # negative value right), which the processor does not promise, and a\n # negative count shifts the other way, as the prelude's `Int32` does.\n def <<(count : Int32) : Int128\n def <=(other : Int8) : Bool\n def <=(other : Int16) : Bool\n def <=(other : Int32) : Bool\n def <=(other : Int64) : Bool\n def <=(other : Int128) : Bool\n def <=(other : UInt8) : Bool\n def <=(other : UInt16) : Bool\n def <=(other : UInt32) : Bool\n def <=(other : UInt64) : Bool\n def <=(other : UInt128) : Bool\n def <=(other : Float64) : Bool\n # Exact for every width: the double is placed against the type's\n # edges, then its floor - one of the type, once inside them - against\n # the value, then its fraction decides a tie. NaN is after every\n # number, as `Float64#<=>` places it. The prelude's `Int64 == Float64`\n # is exact too, and agrees with this on every number.\n def <=>(other : Float64) : Int32\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int64) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int128) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt64) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt128) : Bool\n # A value this wide may not be a double: `9007199254740993_i128 ==\n # 9007199254740992.0` was true, the integer rounded to the double\n # before the comparison. `<=>` below compares exactly.\n # IEEE against NaN: only `!=` is true. `<=>` places NaN for\n # sorting and would answer `<` here.\n def ==(other : Float64) : Bool\n def >(other : Int8) : Bool\n def >(other : Int16) : Bool\n def >(other : Int32) : Bool\n def >(other : Int64) : Bool\n def >(other : Int128) : Bool\n def >(other : UInt8) : Bool\n def >(other : UInt16) : Bool\n def >(other : UInt32) : Bool\n def >(other : UInt64) : Bool\n def >(other : UInt128) : Bool\n def >(other : Float64) : Bool\n def >=(other : Int8) : Bool\n def >=(other : Int16) : Bool\n def >=(other : Int32) : Bool\n def >=(other : Int64) : Bool\n def >=(other : Int128) : Bool\n def >=(other : UInt8) : Bool\n def >=(other : UInt16) : Bool\n def >=(other : UInt32) : Bool\n def >=(other : UInt64) : Bool\n def >=(other : UInt128) : Bool\n def >=(other : Float64) : Bool\n def >>(count : Int32) : Int128\n def ^(other : Int8) : self\n def ^(other : Int16) : self\n def ^(other : Int32) : self\n def ^(other : Int64) : self\n def ^(other : Int128) : self\n def ^(other : UInt8) : self\n def ^(other : UInt16) : self\n def ^(other : UInt32) : self\n def ^(other : UInt64) : self\n def ^(other : UInt128) : self\n def abs : Int128\n # Bits needed to write the value, the sign excluded: `255.bit_length`\n # is 8, `(-1).bit_length` is 0 and `(-2).bit_length` is 1.\n def bit_length : Int32\n def hash : Int32\n def leading_zeros_count : Int32\n # --- the bits, counted with a logical shift for every type ----------\n # A negative value has its sign bits counted: `(-1).popcount` is 32,\n # one loop of the type's width.\n def popcount : Int32\n def to_f : Float64\n def to_f64 : Float64\n def to_i : Int32\n def to_i128 : Int128\n def to_i128? : Int128 | ::Nil\n def to_i16 : Int16\n def to_i16? : Int16 | ::Nil\n def to_i32 : Int32\n def to_i32? : Int32 | ::Nil\n def to_i64 : Int64\n def to_i64? : Int64 | ::Nil\n # Conversions: the checked form panics \"arithmetic overflow\" when the\n # value does not fit; the `?` form answers nil instead; the unchecked\n # form is the instruction - truncated, or extended by the sign of the\n # type it leaves - as the prelude's `unsafe_to_u8` is. A pair the\n # prelude already declares is not declared twice.\n def to_i8 : Int8\n def to_i8? : Int8 | ::Nil\n def to_i? : Int32 | ::Nil\n def to_s(base : Int32) : String\n # Digits backward, the negative magnitude divided rather than\n # negated so the smallest value prints (its negation is not one of\n # the type).\n def to_s : String\n def to_u : UInt32\n def to_u128 : UInt128\n def to_u128? : UInt128 | ::Nil\n def to_u16 : UInt16\n def to_u16? : UInt16 | ::Nil\n def to_u32 : UInt32\n def to_u32? : UInt32 | ::Nil\n def to_u64 : UInt64\n def to_u64? : UInt64 | ::Nil\n def to_u8 : UInt8\n def to_u8? : UInt8 | ::Nil\n def to_u? : UInt32 | ::Nil\n def trailing_zeros_count : Int32\n def unsafe_chr : Char\n def unsafe_div(other : Int8) : self\n def unsafe_div(other : Int16) : self\n def unsafe_div(other : Int32) : self\n def unsafe_div(other : Int64) : self\n def unsafe_div(other : Int128) : self\n def unsafe_div(other : UInt8) : self\n def unsafe_div(other : UInt16) : self\n def unsafe_div(other : UInt32) : self\n def unsafe_div(other : UInt64) : self\n def unsafe_div(other : UInt128) : self\n def unsafe_mod(other : Int8) : self\n def unsafe_mod(other : Int16) : self\n def unsafe_mod(other : Int32) : self\n def unsafe_mod(other : Int64) : self\n def unsafe_mod(other : Int128) : self\n def unsafe_mod(other : UInt8) : self\n def unsafe_mod(other : UInt16) : self\n def unsafe_mod(other : UInt32) : self\n def unsafe_mod(other : UInt64) : self\n def unsafe_mod(other : UInt128) : self\n def unsafe_shl(other : Int8) : self\n def unsafe_shl(other : Int16) : self\n def unsafe_shl(other : Int32) : self\n def unsafe_shl(other : Int64) : self\n def unsafe_shl(other : Int128) : self\n def unsafe_shl(other : UInt8) : self\n def unsafe_shl(other : UInt16) : self\n def unsafe_shl(other : UInt32) : self\n def unsafe_shl(other : UInt64) : self\n def unsafe_shl(other : UInt128) : self\n def unsafe_shr(other : Int8) : self\n def unsafe_shr(other : Int16) : self\n def unsafe_shr(other : Int32) : self\n def unsafe_shr(other : Int64) : self\n def unsafe_shr(other : Int128) : self\n def unsafe_shr(other : UInt8) : self\n def unsafe_shr(other : UInt16) : self\n def unsafe_shr(other : UInt32) : self\n def unsafe_shr(other : UInt64) : self\n def unsafe_shr(other : UInt128) : self\n def unsafe_to_i : Int32\n def unsafe_to_i128 : Int128\n def unsafe_to_i16 : Int16\n def unsafe_to_i32 : Int32\n def unsafe_to_i64 : Int64\n def unsafe_to_i8 : Int8\n def unsafe_to_u : UInt32\n def unsafe_to_u128 : UInt128\n def unsafe_to_u16 : UInt16\n def unsafe_to_u32 : UInt32\n def unsafe_to_u64 : UInt64\n def unsafe_to_u8 : UInt8\n def |(other : Int8) : self\n def |(other : Int16) : self\n def |(other : Int32) : self\n def |(other : Int64) : self\n def |(other : Int128) : self\n def |(other : UInt8) : self\n def |(other : UInt16) : self\n def |(other : UInt32) : self\n def |(other : UInt64) : self\n def |(other : UInt128) : self\n def ~ : Int128\nend\n\nstruct ::Int16\n def !=(other : Int8) : Bool\n def !=(other : Int16) : Bool\n def !=(other : Int32) : Bool\n def !=(other : Int64) : Bool\n def !=(other : Int128) : Bool\n def !=(other : UInt8) : Bool\n def !=(other : UInt16) : Bool\n def !=(other : UInt32) : Bool\n def !=(other : UInt64) : Bool\n def !=(other : UInt128) : Bool\n def !=(other : Float64) : Bool\n def %(other : Int16) : Int16\n def &(other : Int8) : self\n def &(other : Int16) : self\n def &(other : Int32) : self\n def &(other : Int64) : self\n def &(other : Int128) : self\n def &(other : UInt8) : self\n def &(other : UInt16) : self\n def &(other : UInt32) : self\n def &(other : UInt64) : self\n def &(other : UInt128) : self\n def &*(other : Int8) : self\n def &*(other : Int16) : self\n def &*(other : Int32) : self\n def &*(other : Int64) : self\n def &*(other : Int128) : self\n def &*(other : UInt8) : self\n def &*(other : UInt16) : self\n def &*(other : UInt32) : self\n def &*(other : UInt64) : self\n def &*(other : UInt128) : self\n def &+(other : Int8) : self\n def &+(other : Int16) : self\n def &+(other : Int32) : self\n def &+(other : Int64) : self\n def &+(other : Int128) : self\n def &+(other : UInt8) : self\n def &+(other : UInt16) : self\n def &+(other : UInt32) : self\n def &+(other : UInt64) : self\n def &+(other : UInt128) : self\n def &-(other : Int8) : self\n def &-(other : Int16) : self\n def &-(other : Int32) : self\n def &-(other : Int64) : self\n def &-(other : Int128) : self\n def &-(other : UInt8) : self\n def &-(other : UInt16) : self\n def &-(other : UInt32) : self\n def &-(other : UInt64) : self\n def &-(other : UInt128) : self\n def *(other : Int8) : self\n def *(other : Int16) : self\n def *(other : Int32) : self\n def *(other : Int64) : self\n def *(other : Int128) : self\n def *(other : UInt8) : self\n def *(other : UInt16) : self\n def *(other : UInt32) : self\n def *(other : UInt64) : self\n def *(other : UInt128) : self\n def *(other : Float64) : Float64\n def +(other : Int8) : self\n def +(other : Int16) : self\n def +(other : Int32) : self\n def +(other : Int64) : self\n def +(other : Int128) : self\n def +(other : UInt8) : self\n def +(other : UInt16) : self\n def +(other : UInt32) : self\n def +(other : UInt64) : self\n def +(other : UInt128) : self\n def +(other : Float64) : Float64\n def -(other : Int8) : self\n def -(other : Int16) : self\n def -(other : Int32) : self\n def -(other : Int64) : self\n def -(other : Int128) : self\n def -(other : UInt8) : self\n def -(other : UInt16) : self\n def -(other : UInt32) : self\n def -(other : UInt64) : self\n def -(other : UInt128) : self\n def -(other : Float64) : Float64\n def - : Int16\n # An integer divided by a float is a float, as the prelude has it for\n # `Int32` and `Int64` - and for `UInt8` and `UInt64` too, which the\n # prelude does not write: `x / 3.0` did not type for them, and with a\n # literal divisor the `Float32` overload took it, so `1_u8 / 3.0` was a\n # single, 0.33333334.\n def /(other : Float64) : Float64\n # The zero is checked here rather than left to the processor, which\n # reports an integer division by zero as a floating-point fault.\n def //(other : Int16) : Int16\n def <(other : Int8) : Bool\n def <(other : Int16) : Bool\n def <(other : Int32) : Bool\n def <(other : Int64) : Bool\n def <(other : Int128) : Bool\n def <(other : UInt8) : Bool\n def <(other : UInt16) : Bool\n def <(other : UInt32) : Bool\n def <(other : UInt64) : Bool\n def <(other : UInt128) : Bool\n def <(other : Float64) : Bool\n # A shift by the width or more answers zero (or the sign, shifting a\n # negative value right), which the processor does not promise, and a\n # negative count shifts the other way, as the prelude's `Int32` does.\n def <<(count : Int32) : Int16\n def <=(other : Int8) : Bool\n def <=(other : Int16) : Bool\n def <=(other : Int32) : Bool\n def <=(other : Int64) : Bool\n def <=(other : Int128) : Bool\n def <=(other : UInt8) : Bool\n def <=(other : UInt16) : Bool\n def <=(other : UInt32) : Bool\n def <=(other : UInt64) : Bool\n def <=(other : UInt128) : Bool\n def <=(other : Float64) : Bool\n # Exact for every width: the double is placed against the type's\n # edges, then its floor - one of the type, once inside them - against\n # the value, then its fraction decides a tie. NaN is after every\n # number, as `Float64#<=>` places it. The prelude's `Int64 == Float64`\n # is exact too, and agrees with this on every number.\n def <=>(other : Float64) : Int32\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int64) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int128) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt64) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt128) : Bool\n # Every value of the type is a double exactly, so the double's own\n # comparison is the exact one.\n def ==(other : Float64) : Bool\n def >(other : Int8) : Bool\n def >(other : Int16) : Bool\n def >(other : Int32) : Bool\n def >(other : Int64) : Bool\n def >(other : Int128) : Bool\n def >(other : UInt8) : Bool\n def >(other : UInt16) : Bool\n def >(other : UInt32) : Bool\n def >(other : UInt64) : Bool\n def >(other : UInt128) : Bool\n def >(other : Float64) : Bool\n def >=(other : Int8) : Bool\n def >=(other : Int16) : Bool\n def >=(other : Int32) : Bool\n def >=(other : Int64) : Bool\n def >=(other : Int128) : Bool\n def >=(other : UInt8) : Bool\n def >=(other : UInt16) : Bool\n def >=(other : UInt32) : Bool\n def >=(other : UInt64) : Bool\n def >=(other : UInt128) : Bool\n def >=(other : Float64) : Bool\n def >>(count : Int32) : Int16\n def ^(other : Int8) : self\n def ^(other : Int16) : self\n def ^(other : Int32) : self\n def ^(other : Int64) : self\n def ^(other : Int128) : self\n def ^(other : UInt8) : self\n def ^(other : UInt16) : self\n def ^(other : UInt32) : self\n def ^(other : UInt64) : self\n def ^(other : UInt128) : self\n def abs : Int16\n # Bits needed to write the value, the sign excluded: `255.bit_length`\n # is 8, `(-1).bit_length` is 0 and `(-2).bit_length` is 1.\n def bit_length : Int32\n def hash : Int32\n def leading_zeros_count : Int32\n # --- the bits, counted with a logical shift for every type ----------\n # A negative value has its sign bits counted: `(-1).popcount` is 32,\n # one loop of the type's width.\n def popcount : Int32\n def to_f : Float64\n def to_f64 : Float64\n def to_i : Int32\n def to_i128 : Int128\n def to_i128? : Int128 | ::Nil\n def to_i16 : Int16\n def to_i16? : Int16 | ::Nil\n def to_i32 : Int32\n def to_i32? : Int32 | ::Nil\n def to_i64 : Int64\n def to_i64? : Int64 | ::Nil\n # Conversions: the checked form panics \"arithmetic overflow\" when the\n # value does not fit; the `?` form answers nil instead; the unchecked\n # form is the instruction - truncated, or extended by the sign of the\n # type it leaves - as the prelude's `unsafe_to_u8` is. A pair the\n # prelude already declares is not declared twice.\n def to_i8 : Int8\n def to_i8? : Int8 | ::Nil\n def to_i? : Int32 | ::Nil\n def to_s(base : Int32) : String\n # Fits an `Int64`: the prelude's printer.\n def to_s : String\n def to_u : UInt32\n def to_u128 : UInt128\n def to_u128? : UInt128 | ::Nil\n def to_u16 : UInt16\n def to_u16? : UInt16 | ::Nil\n def to_u32 : UInt32\n def to_u32? : UInt32 | ::Nil\n def to_u64 : UInt64\n def to_u64? : UInt64 | ::Nil\n def to_u8 : UInt8\n def to_u8? : UInt8 | ::Nil\n def to_u? : UInt32 | ::Nil\n def trailing_zeros_count : Int32\n def unsafe_chr : Char\n def unsafe_div(other : Int8) : self\n def unsafe_div(other : Int16) : self\n def unsafe_div(other : Int32) : self\n def unsafe_div(other : Int64) : self\n def unsafe_div(other : Int128) : self\n def unsafe_div(other : UInt8) : self\n def unsafe_div(other : UInt16) : self\n def unsafe_div(other : UInt32) : self\n def unsafe_div(other : UInt64) : self\n def unsafe_div(other : UInt128) : self\n def unsafe_mod(other : Int8) : self\n def unsafe_mod(other : Int16) : self\n def unsafe_mod(other : Int32) : self\n def unsafe_mod(other : Int64) : self\n def unsafe_mod(other : Int128) : self\n def unsafe_mod(other : UInt8) : self\n def unsafe_mod(other : UInt16) : self\n def unsafe_mod(other : UInt32) : self\n def unsafe_mod(other : UInt64) : self\n def unsafe_mod(other : UInt128) : self\n def unsafe_shl(other : Int8) : self\n def unsafe_shl(other : Int16) : self\n def unsafe_shl(other : Int32) : self\n def unsafe_shl(other : Int64) : self\n def unsafe_shl(other : Int128) : self\n def unsafe_shl(other : UInt8) : self\n def unsafe_shl(other : UInt16) : self\n def unsafe_shl(other : UInt32) : self\n def unsafe_shl(other : UInt64) : self\n def unsafe_shl(other : UInt128) : self\n def unsafe_shr(other : Int8) : self\n def unsafe_shr(other : Int16) : self\n def unsafe_shr(other : Int32) : self\n def unsafe_shr(other : Int64) : self\n def unsafe_shr(other : Int128) : self\n def unsafe_shr(other : UInt8) : self\n def unsafe_shr(other : UInt16) : self\n def unsafe_shr(other : UInt32) : self\n def unsafe_shr(other : UInt64) : self\n def unsafe_shr(other : UInt128) : self\n def unsafe_to_i : Int32\n def unsafe_to_i128 : Int128\n def unsafe_to_i16 : Int16\n def unsafe_to_i32 : Int32\n def unsafe_to_i64 : Int64\n def unsafe_to_i8 : Int8\n def unsafe_to_u : UInt32\n def unsafe_to_u128 : UInt128\n def unsafe_to_u16 : UInt16\n def unsafe_to_u32 : UInt32\n def unsafe_to_u64 : UInt64\n def unsafe_to_u8 : UInt8\n def |(other : Int8) : self\n def |(other : Int16) : self\n def |(other : Int32) : self\n def |(other : Int64) : self\n def |(other : Int128) : self\n def |(other : UInt8) : self\n def |(other : UInt16) : self\n def |(other : UInt32) : self\n def |(other : UInt64) : self\n def |(other : UInt128) : self\n def ~ : Int16\nend\n\nstruct ::Int32\n def !=(other : Int8) : Bool\n def !=(other : Int16) : Bool\n def !=(other : Int128) : Bool\n def !=(other : UInt32) : Bool\n def !=(other : UInt128) : Bool\n def &(other : Int8) : self\n def &(other : Int16) : self\n def &(other : Int128) : self\n def &(other : UInt16) : self\n def &(other : UInt32) : self\n def &(other : UInt128) : self\n def &*(other : Int8) : self\n def &*(other : Int16) : self\n def &*(other : Int128) : self\n def &*(other : UInt16) : self\n def &*(other : UInt32) : self\n def &*(other : UInt128) : self\n def &+(other : Int8) : self\n def &+(other : Int16) : self\n def &+(other : Int128) : self\n def &+(other : UInt16) : self\n def &+(other : UInt32) : self\n def &+(other : UInt128) : self\n def &-(other : Int8) : self\n def &-(other : Int16) : self\n def &-(other : Int128) : self\n def &-(other : UInt16) : self\n def &-(other : UInt32) : self\n def &-(other : UInt128) : self\n def *(other : Int8) : self\n def *(other : Int16) : self\n def *(other : Int128) : self\n def *(other : UInt16) : self\n def *(other : UInt32) : self\n def *(other : UInt128) : self\n def +(other : Int8) : self\n def +(other : Int16) : self\n def +(other : Int128) : self\n def +(other : UInt16) : self\n def +(other : UInt32) : self\n def +(other : UInt128) : self\n def -(other : Int8) : self\n def -(other : Int16) : self\n def -(other : Int128) : self\n def -(other : UInt16) : self\n def -(other : UInt32) : self\n def -(other : UInt128) : self\n def <(other : Int8) : Bool\n def <(other : Int16) : Bool\n def <(other : Int128) : Bool\n def <(other : UInt32) : Bool\n def <(other : UInt128) : Bool\n def <=(other : Int8) : Bool\n def <=(other : Int16) : Bool\n def <=(other : Int128) : Bool\n def <=(other : UInt32) : Bool\n def <=(other : UInt128) : Bool\n # Exact for every width: the double is placed against the type's\n # edges, then its floor - one of the type, once inside them - against\n # the value, then its fraction decides a tie. NaN is after every\n # number, as `Float64#<=>` places it. The prelude's `Int64 == Float64`\n # is exact too, and agrees with this on every number.\n def <=>(other : Float64) : Int32\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int128) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt128) : Bool\n def >(other : Int8) : Bool\n def >(other : Int16) : Bool\n def >(other : Int128) : Bool\n def >(other : UInt32) : Bool\n def >(other : UInt128) : Bool\n def >=(other : Int8) : Bool\n def >=(other : Int16) : Bool\n def >=(other : Int128) : Bool\n def >=(other : UInt32) : Bool\n def >=(other : UInt128) : Bool\n def ^(other : Int8) : self\n def ^(other : Int16) : self\n def ^(other : Int128) : self\n def ^(other : UInt16) : self\n def ^(other : UInt32) : self\n def ^(other : UInt128) : self\n # Bits needed to write the value, the sign excluded: `255.bit_length`\n # is 8, `(-1).bit_length` is 0 and `(-2).bit_length` is 1.\n def bit_length : Int32\n def leading_zeros_count : Int32\n # --- the bits, counted with a logical shift for every type ----------\n # A negative value has its sign bits counted: `(-1).popcount` is 32,\n # one loop of the type's width.\n def popcount : Int32\n def to_i128 : Int128\n def to_i128? : Int128 | ::Nil\n def to_i16 : Int16\n def to_i16? : Int16 | ::Nil\n def to_i32? : Int32 | ::Nil\n def to_i64? : Int64 | ::Nil\n # Conversions: the checked form panics \"arithmetic overflow\" when the\n # value does not fit; the `?` form answers nil instead; the unchecked\n # form is the instruction - truncated, or extended by the sign of the\n # type it leaves - as the prelude's `unsafe_to_u8` is. A pair the\n # prelude already declares is not declared twice.\n def to_i8 : Int8\n def to_i8? : Int8 | ::Nil\n def to_i? : Int32 | ::Nil\n def to_u : UInt32\n def to_u128 : UInt128\n def to_u128? : UInt128 | ::Nil\n def to_u16 : UInt16\n def to_u16? : UInt16 | ::Nil\n def to_u32 : UInt32\n def to_u32? : UInt32 | ::Nil\n def to_u64? : UInt64 | ::Nil\n def to_u8? : UInt8 | ::Nil\n def to_u? : UInt32 | ::Nil\n def trailing_zeros_count : Int32\n def unsafe_div(other : Int8) : self\n def unsafe_div(other : Int16) : self\n def unsafe_div(other : Int128) : self\n def unsafe_div(other : UInt16) : self\n def unsafe_div(other : UInt32) : self\n def unsafe_div(other : UInt128) : self\n def unsafe_mod(other : Int8) : self\n def unsafe_mod(other : Int16) : self\n def unsafe_mod(other : Int128) : self\n def unsafe_mod(other : UInt16) : self\n def unsafe_mod(other : UInt32) : self\n def unsafe_mod(other : UInt128) : self\n def unsafe_shl(other : Int8) : self\n def unsafe_shl(other : Int16) : self\n def unsafe_shl(other : Int128) : self\n def unsafe_shl(other : UInt16) : self\n def unsafe_shl(other : UInt32) : self\n def unsafe_shl(other : UInt128) : self\n def unsafe_shr(other : Int8) : self\n def unsafe_shr(other : Int16) : self\n def unsafe_shr(other : Int128) : self\n def unsafe_shr(other : UInt16) : self\n def unsafe_shr(other : UInt32) : self\n def unsafe_shr(other : UInt128) : self\n def unsafe_to_i128 : Int128\n def unsafe_to_i16 : Int16\n def unsafe_to_i8 : Int8\n def unsafe_to_u : UInt32\n def unsafe_to_u128 : UInt128\n def unsafe_to_u16 : UInt16\n def unsafe_to_u32 : UInt32\n def |(other : Int8) : self\n def |(other : Int16) : self\n def |(other : Int128) : self\n def |(other : UInt16) : self\n def |(other : UInt32) : self\n def |(other : UInt128) : self\n def ~ : Int32\nend\n\nstruct ::Int64\n def !=(other : Int8) : Bool\n def !=(other : Int16) : Bool\n def !=(other : Int128) : Bool\n def !=(other : UInt32) : Bool\n def !=(other : UInt128) : Bool\n def &(other : Int8) : self\n def &(other : Int16) : self\n def &(other : Int128) : self\n def &(other : UInt16) : self\n def &(other : UInt32) : self\n def &(other : UInt128) : self\n def &*(other : Int8) : self\n def &*(other : Int16) : self\n def &*(other : Int128) : self\n def &*(other : UInt16) : self\n def &*(other : UInt32) : self\n def &*(other : UInt128) : self\n def &+(other : Int8) : self\n def &+(other : Int16) : self\n def &+(other : Int128) : self\n def &+(other : UInt16) : self\n def &+(other : UInt32) : self\n def &+(other : UInt128) : self\n def &-(other : Int8) : self\n def &-(other : Int16) : self\n def &-(other : Int128) : self\n def &-(other : UInt16) : self\n def &-(other : UInt32) : self\n def &-(other : UInt128) : self\n def *(other : Int8) : self\n def *(other : Int16) : self\n def *(other : Int128) : self\n def *(other : UInt16) : self\n def *(other : UInt32) : self\n def *(other : UInt128) : self\n def +(other : Int8) : self\n def +(other : Int16) : self\n def +(other : Int128) : self\n def +(other : UInt16) : self\n def +(other : UInt32) : self\n def +(other : UInt128) : self\n def -(other : Int8) : self\n def -(other : Int16) : self\n def -(other : Int128) : self\n def -(other : UInt16) : self\n def -(other : UInt32) : self\n def -(other : UInt128) : self\n def <(other : Int8) : Bool\n def <(other : Int16) : Bool\n def <(other : Int128) : Bool\n def <(other : UInt32) : Bool\n def <(other : UInt128) : Bool\n def <=(other : Int8) : Bool\n def <=(other : Int16) : Bool\n def <=(other : Int128) : Bool\n def <=(other : UInt32) : Bool\n def <=(other : UInt128) : Bool\n # Exact for every width: the double is placed against the type's\n # edges, then its floor - one of the type, once inside them - against\n # the value, then its fraction decides a tie. NaN is after every\n # number, as `Float64#<=>` places it. The prelude's `Int64 == Float64`\n # is exact too, and agrees with this on every number.\n def <=>(other : Float64) : Int32\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int128) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt128) : Bool\n def >(other : Int8) : Bool\n def >(other : Int16) : Bool\n def >(other : Int128) : Bool\n def >(other : UInt32) : Bool\n def >(other : UInt128) : Bool\n def >=(other : Int8) : Bool\n def >=(other : Int16) : Bool\n def >=(other : Int128) : Bool\n def >=(other : UInt32) : Bool\n def >=(other : UInt128) : Bool\n def ^(other : Int8) : self\n def ^(other : Int16) : self\n def ^(other : Int128) : self\n def ^(other : UInt16) : self\n def ^(other : UInt32) : self\n def ^(other : UInt128) : self\n # Bits needed to write the value, the sign excluded: `255.bit_length`\n # is 8, `(-1).bit_length` is 0 and `(-2).bit_length` is 1.\n def bit_length : Int32\n def leading_zeros_count : Int32\n # --- the bits, counted with a logical shift for every type ----------\n # A negative value has its sign bits counted: `(-1).popcount` is 32,\n # one loop of the type's width.\n def popcount : Int32\n def to_i128 : Int128\n def to_i128? : Int128 | ::Nil\n def to_i16 : Int16\n def to_i16? : Int16 | ::Nil\n def to_i32? : Int32 | ::Nil\n def to_i64? : Int64 | ::Nil\n # Conversions: the checked form panics \"arithmetic overflow\" when the\n # value does not fit; the `?` form answers nil instead; the unchecked\n # form is the instruction - truncated, or extended by the sign of the\n # type it leaves - as the prelude's `unsafe_to_u8` is. A pair the\n # prelude already declares is not declared twice.\n def to_i8 : Int8\n def to_i8? : Int8 | ::Nil\n def to_i? : Int32 | ::Nil\n def to_u : UInt32\n def to_u128 : UInt128\n def to_u128? : UInt128 | ::Nil\n def to_u16 : UInt16\n def to_u16? : UInt16 | ::Nil\n def to_u32 : UInt32\n def to_u32? : UInt32 | ::Nil\n def to_u64? : UInt64 | ::Nil\n def to_u8? : UInt8 | ::Nil\n def to_u? : UInt32 | ::Nil\n def trailing_zeros_count : Int32\n def unsafe_div(other : Int8) : self\n def unsafe_div(other : Int16) : self\n def unsafe_div(other : Int128) : self\n def unsafe_div(other : UInt16) : self\n def unsafe_div(other : UInt32) : self\n def unsafe_div(other : UInt128) : self\n def unsafe_mod(other : Int8) : self\n def unsafe_mod(other : Int16) : self\n def unsafe_mod(other : Int128) : self\n def unsafe_mod(other : UInt16) : self\n def unsafe_mod(other : UInt32) : self\n def unsafe_mod(other : UInt128) : self\n def unsafe_shl(other : Int8) : self\n def unsafe_shl(other : Int16) : self\n def unsafe_shl(other : Int128) : self\n def unsafe_shl(other : UInt16) : self\n def unsafe_shl(other : UInt32) : self\n def unsafe_shl(other : UInt128) : self\n def unsafe_shr(other : Int8) : self\n def unsafe_shr(other : Int16) : self\n def unsafe_shr(other : Int128) : self\n def unsafe_shr(other : UInt16) : self\n def unsafe_shr(other : UInt32) : self\n def unsafe_shr(other : UInt128) : self\n def unsafe_to_i128 : Int128\n def unsafe_to_i16 : Int16\n def unsafe_to_i8 : Int8\n def unsafe_to_u : UInt32\n def unsafe_to_u128 : UInt128\n def unsafe_to_u16 : UInt16\n def unsafe_to_u32 : UInt32\n def |(other : Int8) : self\n def |(other : Int16) : self\n def |(other : Int128) : self\n def |(other : UInt16) : self\n def |(other : UInt32) : self\n def |(other : UInt128) : self\n def ~ : Int64\nend\n\nstruct ::Int8\n def !=(other : Int8) : Bool\n def !=(other : Int16) : Bool\n def !=(other : Int32) : Bool\n def !=(other : Int64) : Bool\n def !=(other : Int128) : Bool\n def !=(other : UInt8) : Bool\n def !=(other : UInt16) : Bool\n def !=(other : UInt32) : Bool\n def !=(other : UInt64) : Bool\n def !=(other : UInt128) : Bool\n def !=(other : Float64) : Bool\n def %(other : Int8) : Int8\n def &(other : Int8) : self\n def &(other : Int16) : self\n def &(other : Int32) : self\n def &(other : Int64) : self\n def &(other : Int128) : self\n def &(other : UInt8) : self\n def &(other : UInt16) : self\n def &(other : UInt32) : self\n def &(other : UInt64) : self\n def &(other : UInt128) : self\n def &*(other : Int8) : self\n def &*(other : Int16) : self\n def &*(other : Int32) : self\n def &*(other : Int64) : self\n def &*(other : Int128) : self\n def &*(other : UInt8) : self\n def &*(other : UInt16) : self\n def &*(other : UInt32) : self\n def &*(other : UInt64) : self\n def &*(other : UInt128) : self\n def &+(other : Int8) : self\n def &+(other : Int16) : self\n def &+(other : Int32) : self\n def &+(other : Int64) : self\n def &+(other : Int128) : self\n def &+(other : UInt8) : self\n def &+(other : UInt16) : self\n def &+(other : UInt32) : self\n def &+(other : UInt64) : self\n def &+(other : UInt128) : self\n def &-(other : Int8) : self\n def &-(other : Int16) : self\n def &-(other : Int32) : self\n def &-(other : Int64) : self\n def &-(other : Int128) : self\n def &-(other : UInt8) : self\n def &-(other : UInt16) : self\n def &-(other : UInt32) : self\n def &-(other : UInt64) : self\n def &-(other : UInt128) : self\n def *(other : Int8) : self\n def *(other : Int16) : self\n def *(other : Int32) : self\n def *(other : Int64) : self\n def *(other : Int128) : self\n def *(other : UInt8) : self\n def *(other : UInt16) : self\n def *(other : UInt32) : self\n def *(other : UInt64) : self\n def *(other : UInt128) : self\n def *(other : Float64) : Float64\n def +(other : Int8) : self\n def +(other : Int16) : self\n def +(other : Int32) : self\n def +(other : Int64) : self\n def +(other : Int128) : self\n def +(other : UInt8) : self\n def +(other : UInt16) : self\n def +(other : UInt32) : self\n def +(other : UInt64) : self\n def +(other : UInt128) : self\n def +(other : Float64) : Float64\n def -(other : Int8) : self\n def -(other : Int16) : self\n def -(other : Int32) : self\n def -(other : Int64) : self\n def -(other : Int128) : self\n def -(other : UInt8) : self\n def -(other : UInt16) : self\n def -(other : UInt32) : self\n def -(other : UInt64) : self\n def -(other : UInt128) : self\n def -(other : Float64) : Float64\n def - : Int8\n # An integer divided by a float is a float, as the prelude has it for\n # `Int32` and `Int64` - and for `UInt8` and `UInt64` too, which the\n # prelude does not write: `x / 3.0` did not type for them, and with a\n # literal divisor the `Float32` overload took it, so `1_u8 / 3.0` was a\n # single, 0.33333334.\n def /(other : Float64) : Float64\n # The zero is checked here rather than left to the processor, which\n # reports an integer division by zero as a floating-point fault.\n def //(other : Int8) : Int8\n def <(other : Int8) : Bool\n def <(other : Int16) : Bool\n def <(other : Int32) : Bool\n def <(other : Int64) : Bool\n def <(other : Int128) : Bool\n def <(other : UInt8) : Bool\n def <(other : UInt16) : Bool\n def <(other : UInt32) : Bool\n def <(other : UInt64) : Bool\n def <(other : UInt128) : Bool\n def <(other : Float64) : Bool\n # A shift by the width or more answers zero (or the sign, shifting a\n # negative value right), which the processor does not promise, and a\n # negative count shifts the other way, as the prelude's `Int32` does.\n def <<(count : Int32) : Int8\n def <=(other : Int8) : Bool\n def <=(other : Int16) : Bool\n def <=(other : Int32) : Bool\n def <=(other : Int64) : Bool\n def <=(other : Int128) : Bool\n def <=(other : UInt8) : Bool\n def <=(other : UInt16) : Bool\n def <=(other : UInt32) : Bool\n def <=(other : UInt64) : Bool\n def <=(other : UInt128) : Bool\n def <=(other : Float64) : Bool\n # Exact for every width: the double is placed against the type's\n # edges, then its floor - one of the type, once inside them - against\n # the value, then its fraction decides a tie. NaN is after every\n # number, as `Float64#<=>` places it. The prelude's `Int64 == Float64`\n # is exact too, and agrees with this on every number.\n def <=>(other : Float64) : Int32\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int64) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int128) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt64) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt128) : Bool\n # Every value of the type is a double exactly, so the double's own\n # comparison is the exact one.\n def ==(other : Float64) : Bool\n def >(other : Int8) : Bool\n def >(other : Int16) : Bool\n def >(other : Int32) : Bool\n def >(other : Int64) : Bool\n def >(other : Int128) : Bool\n def >(other : UInt8) : Bool\n def >(other : UInt16) : Bool\n def >(other : UInt32) : Bool\n def >(other : UInt64) : Bool\n def >(other : UInt128) : Bool\n def >(other : Float64) : Bool\n def >=(other : Int8) : Bool\n def >=(other : Int16) : Bool\n def >=(other : Int32) : Bool\n def >=(other : Int64) : Bool\n def >=(other : Int128) : Bool\n def >=(other : UInt8) : Bool\n def >=(other : UInt16) : Bool\n def >=(other : UInt32) : Bool\n def >=(other : UInt64) : Bool\n def >=(other : UInt128) : Bool\n def >=(other : Float64) : Bool\n def >>(count : Int32) : Int8\n def ^(other : Int8) : self\n def ^(other : Int16) : self\n def ^(other : Int32) : self\n def ^(other : Int64) : self\n def ^(other : Int128) : self\n def ^(other : UInt8) : self\n def ^(other : UInt16) : self\n def ^(other : UInt32) : self\n def ^(other : UInt64) : self\n def ^(other : UInt128) : self\n def abs : Int8\n # Bits needed to write the value, the sign excluded: `255.bit_length`\n # is 8, `(-1).bit_length` is 0 and `(-2).bit_length` is 1.\n def bit_length : Int32\n def hash : Int32\n def leading_zeros_count : Int32\n # --- the bits, counted with a logical shift for every type ----------\n # A negative value has its sign bits counted: `(-1).popcount` is 32,\n # one loop of the type's width.\n def popcount : Int32\n def to_f : Float64\n def to_f64 : Float64\n def to_i : Int32\n def to_i128 : Int128\n def to_i128? : Int128 | ::Nil\n def to_i16 : Int16\n def to_i16? : Int16 | ::Nil\n def to_i32 : Int32\n def to_i32? : Int32 | ::Nil\n def to_i64 : Int64\n def to_i64? : Int64 | ::Nil\n # Conversions: the checked form panics \"arithmetic overflow\" when the\n # value does not fit; the `?` form answers nil instead; the unchecked\n # form is the instruction - truncated, or extended by the sign of the\n # type it leaves - as the prelude's `unsafe_to_u8` is. A pair the\n # prelude already declares is not declared twice.\n def to_i8 : Int8\n def to_i8? : Int8 | ::Nil\n def to_i? : Int32 | ::Nil\n def to_s(base : Int32) : String\n # Fits an `Int64`: the prelude's printer.\n def to_s : String\n def to_u : UInt32\n def to_u128 : UInt128\n def to_u128? : UInt128 | ::Nil\n def to_u16 : UInt16\n def to_u16? : UInt16 | ::Nil\n def to_u32 : UInt32\n def to_u32? : UInt32 | ::Nil\n def to_u64 : UInt64\n def to_u64? : UInt64 | ::Nil\n def to_u8 : UInt8\n def to_u8? : UInt8 | ::Nil\n def to_u? : UInt32 | ::Nil\n def trailing_zeros_count : Int32\n def unsafe_chr : Char\n def unsafe_div(other : Int8) : self\n def unsafe_div(other : Int16) : self\n def unsafe_div(other : Int32) : self\n def unsafe_div(other : Int64) : self\n def unsafe_div(other : Int128) : self\n def unsafe_div(other : UInt8) : self\n def unsafe_div(other : UInt16) : self\n def unsafe_div(other : UInt32) : self\n def unsafe_div(other : UInt64) : self\n def unsafe_div(other : UInt128) : self\n def unsafe_mod(other : Int8) : self\n def unsafe_mod(other : Int16) : self\n def unsafe_mod(other : Int32) : self\n def unsafe_mod(other : Int64) : self\n def unsafe_mod(other : Int128) : self\n def unsafe_mod(other : UInt8) : self\n def unsafe_mod(other : UInt16) : self\n def unsafe_mod(other : UInt32) : self\n def unsafe_mod(other : UInt64) : self\n def unsafe_mod(other : UInt128) : self\n def unsafe_shl(other : Int8) : self\n def unsafe_shl(other : Int16) : self\n def unsafe_shl(other : Int32) : self\n def unsafe_shl(other : Int64) : self\n def unsafe_shl(other : Int128) : self\n def unsafe_shl(other : UInt8) : self\n def unsafe_shl(other : UInt16) : self\n def unsafe_shl(other : UInt32) : self\n def unsafe_shl(other : UInt64) : self\n def unsafe_shl(other : UInt128) : self\n def unsafe_shr(other : Int8) : self\n def unsafe_shr(other : Int16) : self\n def unsafe_shr(other : Int32) : self\n def unsafe_shr(other : Int64) : self\n def unsafe_shr(other : Int128) : self\n def unsafe_shr(other : UInt8) : self\n def unsafe_shr(other : UInt16) : self\n def unsafe_shr(other : UInt32) : self\n def unsafe_shr(other : UInt64) : self\n def unsafe_shr(other : UInt128) : self\n def unsafe_to_i : Int32\n def unsafe_to_i128 : Int128\n def unsafe_to_i16 : Int16\n def unsafe_to_i32 : Int32\n def unsafe_to_i64 : Int64\n def unsafe_to_i8 : Int8\n def unsafe_to_u : UInt32\n def unsafe_to_u128 : UInt128\n def unsafe_to_u16 : UInt16\n def unsafe_to_u32 : UInt32\n def unsafe_to_u64 : UInt64\n def unsafe_to_u8 : UInt8\n def |(other : Int8) : self\n def |(other : Int16) : self\n def |(other : Int32) : self\n def |(other : Int64) : self\n def |(other : Int128) : self\n def |(other : UInt8) : self\n def |(other : UInt16) : self\n def |(other : UInt32) : self\n def |(other : UInt64) : self\n def |(other : UInt128) : self\n def ~ : Int8\nend\n\nstruct ::UInt128\n def !=(other : Int8) : Bool\n def !=(other : Int16) : Bool\n def !=(other : Int32) : Bool\n def !=(other : Int64) : Bool\n def !=(other : Int128) : Bool\n def !=(other : UInt8) : Bool\n def !=(other : UInt16) : Bool\n def !=(other : UInt32) : Bool\n def !=(other : UInt64) : Bool\n def !=(other : UInt128) : Bool\n def !=(other : Float64) : Bool\n def %(other : UInt128) : UInt128\n def &(other : Int8) : self\n def &(other : Int16) : self\n def &(other : Int32) : self\n def &(other : Int64) : self\n def &(other : Int128) : self\n def &(other : UInt8) : self\n def &(other : UInt16) : self\n def &(other : UInt32) : self\n def &(other : UInt64) : self\n def &(other : UInt128) : self\n def &*(other : Int8) : self\n def &*(other : Int16) : self\n def &*(other : Int32) : self\n def &*(other : Int64) : self\n def &*(other : Int128) : self\n def &*(other : UInt8) : self\n def &*(other : UInt16) : self\n def &*(other : UInt32) : self\n def &*(other : UInt64) : self\n def &*(other : UInt128) : self\n def &+(other : Int8) : self\n def &+(other : Int16) : self\n def &+(other : Int32) : self\n def &+(other : Int64) : self\n def &+(other : Int128) : self\n def &+(other : UInt8) : self\n def &+(other : UInt16) : self\n def &+(other : UInt32) : self\n def &+(other : UInt64) : self\n def &+(other : UInt128) : self\n def &-(other : Int8) : self\n def &-(other : Int16) : self\n def &-(other : Int32) : self\n def &-(other : Int64) : self\n def &-(other : Int128) : self\n def &-(other : UInt8) : self\n def &-(other : UInt16) : self\n def &-(other : UInt32) : self\n def &-(other : UInt64) : self\n def &-(other : UInt128) : self\n def *(other : Int8) : self\n def *(other : Int16) : self\n def *(other : Int32) : self\n def *(other : Int64) : self\n def *(other : Int128) : self\n def *(other : UInt8) : self\n def *(other : UInt16) : self\n def *(other : UInt32) : self\n def *(other : UInt64) : self\n def *(other : UInt128) : self\n def *(other : Float64) : Float64\n def +(other : Int8) : self\n def +(other : Int16) : self\n def +(other : Int32) : self\n def +(other : Int64) : self\n def +(other : Int128) : self\n def +(other : UInt8) : self\n def +(other : UInt16) : self\n def +(other : UInt32) : self\n def +(other : UInt64) : self\n def +(other : UInt128) : self\n def +(other : Float64) : Float64\n def -(other : Int8) : self\n def -(other : Int16) : self\n def -(other : Int32) : self\n def -(other : Int64) : self\n def -(other : Int128) : self\n def -(other : UInt8) : self\n def -(other : UInt16) : self\n def -(other : UInt32) : self\n def -(other : UInt64) : self\n def -(other : UInt128) : self\n def -(other : Float64) : Float64\n # An integer divided by a float is a float, as the prelude has it for\n # `Int32` and `Int64` - and for `UInt8` and `UInt64` too, which the\n # prelude does not write: `x / 3.0` did not type for them, and with a\n # literal divisor the `Float32` overload took it, so `1_u8 / 3.0` was a\n # single, 0.33333334.\n def /(other : Float64) : Float64\n # The zero is checked here rather than left to the processor, which\n # reports an integer division by zero as a floating-point fault.\n def //(other : UInt128) : UInt128\n def <(other : Int8) : Bool\n def <(other : Int16) : Bool\n def <(other : Int32) : Bool\n def <(other : Int64) : Bool\n def <(other : Int128) : Bool\n def <(other : UInt8) : Bool\n def <(other : UInt16) : Bool\n def <(other : UInt32) : Bool\n def <(other : UInt64) : Bool\n def <(other : UInt128) : Bool\n def <(other : Float64) : Bool\n # A shift by the width or more answers zero (or the sign, shifting a\n # negative value right), which the processor does not promise, and a\n # negative count shifts the other way, as the prelude's `Int32` does.\n def <<(count : Int32) : UInt128\n def <=(other : Int8) : Bool\n def <=(other : Int16) : Bool\n def <=(other : Int32) : Bool\n def <=(other : Int64) : Bool\n def <=(other : Int128) : Bool\n def <=(other : UInt8) : Bool\n def <=(other : UInt16) : Bool\n def <=(other : UInt32) : Bool\n def <=(other : UInt64) : Bool\n def <=(other : UInt128) : Bool\n def <=(other : Float64) : Bool\n # Exact for every width: the double is placed against the type's\n # edges, then its floor - one of the type, once inside them - against\n # the value, then its fraction decides a tie. NaN is after every\n # number, as `Float64#<=>` places it. The prelude's `Int64 == Float64`\n # is exact too, and agrees with this on every number.\n def <=>(other : Float64) : Int32\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int64) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int128) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt64) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt128) : Bool\n # A value this wide may not be a double: `9007199254740993_i128 ==\n # 9007199254740992.0` was true, the integer rounded to the double\n # before the comparison. `<=>` below compares exactly.\n # IEEE against NaN: only `!=` is true. `<=>` places NaN for\n # sorting and would answer `<` here.\n def ==(other : Float64) : Bool\n def >(other : Int8) : Bool\n def >(other : Int16) : Bool\n def >(other : Int32) : Bool\n def >(other : Int64) : Bool\n def >(other : Int128) : Bool\n def >(other : UInt8) : Bool\n def >(other : UInt16) : Bool\n def >(other : UInt32) : Bool\n def >(other : UInt64) : Bool\n def >(other : UInt128) : Bool\n def >(other : Float64) : Bool\n def >=(other : Int8) : Bool\n def >=(other : Int16) : Bool\n def >=(other : Int32) : Bool\n def >=(other : Int64) : Bool\n def >=(other : Int128) : Bool\n def >=(other : UInt8) : Bool\n def >=(other : UInt16) : Bool\n def >=(other : UInt32) : Bool\n def >=(other : UInt64) : Bool\n def >=(other : UInt128) : Bool\n def >=(other : Float64) : Bool\n def >>(count : Int32) : UInt128\n def ^(other : Int8) : self\n def ^(other : Int16) : self\n def ^(other : Int32) : self\n def ^(other : Int64) : self\n def ^(other : Int128) : self\n def ^(other : UInt8) : self\n def ^(other : UInt16) : self\n def ^(other : UInt32) : self\n def ^(other : UInt64) : self\n def ^(other : UInt128) : self\n def abs : UInt128\n # Bits needed to write the value, the sign excluded: `255.bit_length`\n # is 8, `(-1).bit_length` is 0 and `(-2).bit_length` is 1.\n def bit_length : Int32\n def hash : Int32\n def leading_zeros_count : Int32\n # --- the bits, counted with a logical shift for every type ----------\n # A negative value has its sign bits counted: `(-1).popcount` is 32,\n # one loop of the type's width.\n def popcount : Int32\n def to_f : Float64\n def to_f64 : Float64\n def to_i : Int32\n def to_i128 : Int128\n def to_i128? : Int128 | ::Nil\n def to_i16 : Int16\n def to_i16? : Int16 | ::Nil\n def to_i32 : Int32\n def to_i32? : Int32 | ::Nil\n def to_i64 : Int64\n def to_i64? : Int64 | ::Nil\n # Conversions: the checked form panics \"arithmetic overflow\" when the\n # value does not fit; the `?` form answers nil instead; the unchecked\n # form is the instruction - truncated, or extended by the sign of the\n # type it leaves - as the prelude's `unsafe_to_u8` is. A pair the\n # prelude already declares is not declared twice.\n def to_i8 : Int8\n def to_i8? : Int8 | ::Nil\n def to_i? : Int32 | ::Nil\n def to_s(base : Int32) : String\n # Digits backward, the negative magnitude divided rather than\n # negated so the smallest value prints (its negation is not one of\n # the type).\n def to_s : String\n def to_u : UInt32\n def to_u128 : UInt128\n def to_u128? : UInt128 | ::Nil\n def to_u16 : UInt16\n def to_u16? : UInt16 | ::Nil\n def to_u32 : UInt32\n def to_u32? : UInt32 | ::Nil\n def to_u64 : UInt64\n def to_u64? : UInt64 | ::Nil\n def to_u8 : UInt8\n def to_u8? : UInt8 | ::Nil\n def to_u? : UInt32 | ::Nil\n def trailing_zeros_count : Int32\n def unsafe_chr : Char\n def unsafe_div(other : Int8) : self\n def unsafe_div(other : Int16) : self\n def unsafe_div(other : Int32) : self\n def unsafe_div(other : Int64) : self\n def unsafe_div(other : Int128) : self\n def unsafe_div(other : UInt8) : self\n def unsafe_div(other : UInt16) : self\n def unsafe_div(other : UInt32) : self\n def unsafe_div(other : UInt64) : self\n def unsafe_div(other : UInt128) : self\n def unsafe_mod(other : Int8) : self\n def unsafe_mod(other : Int16) : self\n def unsafe_mod(other : Int32) : self\n def unsafe_mod(other : Int64) : self\n def unsafe_mod(other : Int128) : self\n def unsafe_mod(other : UInt8) : self\n def unsafe_mod(other : UInt16) : self\n def unsafe_mod(other : UInt32) : self\n def unsafe_mod(other : UInt64) : self\n def unsafe_mod(other : UInt128) : self\n def unsafe_shl(other : Int8) : self\n def unsafe_shl(other : Int16) : self\n def unsafe_shl(other : Int32) : self\n def unsafe_shl(other : Int64) : self\n def unsafe_shl(other : Int128) : self\n def unsafe_shl(other : UInt8) : self\n def unsafe_shl(other : UInt16) : self\n def unsafe_shl(other : UInt32) : self\n def unsafe_shl(other : UInt64) : self\n def unsafe_shl(other : UInt128) : self\n def unsafe_shr(other : Int8) : self\n def unsafe_shr(other : Int16) : self\n def unsafe_shr(other : Int32) : self\n def unsafe_shr(other : Int64) : self\n def unsafe_shr(other : Int128) : self\n def unsafe_shr(other : UInt8) : self\n def unsafe_shr(other : UInt16) : self\n def unsafe_shr(other : UInt32) : self\n def unsafe_shr(other : UInt64) : self\n def unsafe_shr(other : UInt128) : self\n def unsafe_to_i : Int32\n def unsafe_to_i128 : Int128\n def unsafe_to_i16 : Int16\n def unsafe_to_i32 : Int32\n def unsafe_to_i64 : Int64\n def unsafe_to_i8 : Int8\n def unsafe_to_u : UInt32\n def unsafe_to_u128 : UInt128\n def unsafe_to_u16 : UInt16\n def unsafe_to_u32 : UInt32\n def unsafe_to_u64 : UInt64\n def unsafe_to_u8 : UInt8\n def |(other : Int8) : self\n def |(other : Int16) : self\n def |(other : Int32) : self\n def |(other : Int64) : self\n def |(other : Int128) : self\n def |(other : UInt8) : self\n def |(other : UInt16) : self\n def |(other : UInt32) : self\n def |(other : UInt64) : self\n def |(other : UInt128) : self\n def ~ : UInt128\nend\n\nstruct ::UInt16\n def !=(other : Int8) : Bool\n def !=(other : Int16) : Bool\n def !=(other : Int128) : Bool\n def !=(other : UInt32) : Bool\n def !=(other : UInt128) : Bool\n def !=(other : Float64) : Bool\n def %(other : UInt16) : UInt16\n def &(other : Int8) : self\n def &(other : Int16) : self\n def &(other : Int32) : self\n def &(other : Int64) : self\n def &(other : Int128) : self\n def &(other : UInt8) : self\n def &(other : UInt16) : self\n def &(other : UInt32) : self\n def &(other : UInt64) : self\n def &(other : UInt128) : self\n def &*(other : Int8) : self\n def &*(other : Int16) : self\n def &*(other : Int32) : self\n def &*(other : Int64) : self\n def &*(other : Int128) : self\n def &*(other : UInt8) : self\n def &*(other : UInt16) : self\n def &*(other : UInt32) : self\n def &*(other : UInt64) : self\n def &*(other : UInt128) : self\n def &+(other : Int8) : self\n def &+(other : Int16) : self\n def &+(other : Int32) : self\n def &+(other : Int64) : self\n def &+(other : Int128) : self\n def &+(other : UInt8) : self\n def &+(other : UInt16) : self\n def &+(other : UInt32) : self\n def &+(other : UInt64) : self\n def &+(other : UInt128) : self\n def &-(other : Int8) : self\n def &-(other : Int16) : self\n def &-(other : Int32) : self\n def &-(other : Int64) : self\n def &-(other : Int128) : self\n def &-(other : UInt8) : self\n def &-(other : UInt16) : self\n def &-(other : UInt32) : self\n def &-(other : UInt64) : self\n def &-(other : UInt128) : self\n def *(other : Int8) : self\n def *(other : Int16) : self\n def *(other : Int32) : self\n def *(other : Int64) : self\n def *(other : Int128) : self\n def *(other : UInt8) : self\n def *(other : UInt16) : self\n def *(other : UInt32) : self\n def *(other : UInt64) : self\n def *(other : UInt128) : self\n def *(other : Float64) : Float64\n def +(other : Int8) : self\n def +(other : Int16) : self\n def +(other : Int32) : self\n def +(other : Int64) : self\n def +(other : Int128) : self\n def +(other : UInt8) : self\n def +(other : UInt16) : self\n def +(other : UInt32) : self\n def +(other : UInt64) : self\n def +(other : UInt128) : self\n def +(other : Float64) : Float64\n def -(other : Int8) : self\n def -(other : Int16) : self\n def -(other : Int32) : self\n def -(other : Int64) : self\n def -(other : Int128) : self\n def -(other : UInt8) : self\n def -(other : UInt16) : self\n def -(other : UInt32) : self\n def -(other : UInt64) : self\n def -(other : UInt128) : self\n def -(other : Float64) : Float64\n # An integer divided by a float is a float, as the prelude has it for\n # `Int32` and `Int64` - and for `UInt8` and `UInt64` too, which the\n # prelude does not write: `x / 3.0` did not type for them, and with a\n # literal divisor the `Float32` overload took it, so `1_u8 / 3.0` was a\n # single, 0.33333334.\n def /(other : Float64) : Float64\n # The zero is checked here rather than left to the processor, which\n # reports an integer division by zero as a floating-point fault.\n def //(other : UInt16) : UInt16\n def <(other : Int8) : Bool\n def <(other : Int16) : Bool\n def <(other : Int128) : Bool\n def <(other : UInt32) : Bool\n def <(other : UInt128) : Bool\n def <(other : Float64) : Bool\n # A shift by the width or more answers zero (or the sign, shifting a\n # negative value right), which the processor does not promise, and a\n # negative count shifts the other way, as the prelude's `Int32` does.\n def <<(count : Int32) : UInt16\n def <=(other : Int8) : Bool\n def <=(other : Int16) : Bool\n def <=(other : Int128) : Bool\n def <=(other : UInt32) : Bool\n def <=(other : UInt128) : Bool\n def <=(other : Float64) : Bool\n # Exact for every width: the double is placed against the type's\n # edges, then its floor - one of the type, once inside them - against\n # the value, then its fraction decides a tie. NaN is after every\n # number, as `Float64#<=>` places it. The prelude's `Int64 == Float64`\n # is exact too, and agrees with this on every number.\n def <=>(other : Float64) : Int32\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int128) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt128) : Bool\n # Every value of the type is a double exactly, so the double's own\n # comparison is the exact one.\n def ==(other : Float64) : Bool\n def >(other : Int8) : Bool\n def >(other : Int16) : Bool\n def >(other : Int128) : Bool\n def >(other : UInt32) : Bool\n def >(other : UInt128) : Bool\n def >(other : Float64) : Bool\n def >=(other : Int8) : Bool\n def >=(other : Int16) : Bool\n def >=(other : Int128) : Bool\n def >=(other : UInt32) : Bool\n def >=(other : UInt128) : Bool\n def >=(other : Float64) : Bool\n def >>(count : Int32) : UInt16\n def ^(other : Int8) : self\n def ^(other : Int16) : self\n def ^(other : Int32) : self\n def ^(other : Int64) : self\n def ^(other : Int128) : self\n def ^(other : UInt8) : self\n def ^(other : UInt16) : self\n def ^(other : UInt32) : self\n def ^(other : UInt64) : self\n def ^(other : UInt128) : self\n def abs : UInt16\n # Bits needed to write the value, the sign excluded: `255.bit_length`\n # is 8, `(-1).bit_length` is 0 and `(-2).bit_length` is 1.\n def bit_length : Int32\n def hash : Int32\n def leading_zeros_count : Int32\n # --- the bits, counted with a logical shift for every type ----------\n # A negative value has its sign bits counted: `(-1).popcount` is 32,\n # one loop of the type's width.\n def popcount : Int32\n def to_f : Float64\n def to_f64 : Float64\n def to_i : Int32\n def to_i128 : Int128\n def to_i128? : Int128 | ::Nil\n def to_i16 : Int16\n def to_i16? : Int16 | ::Nil\n def to_i32 : Int32\n def to_i32? : Int32 | ::Nil\n def to_i64 : Int64\n def to_i64? : Int64 | ::Nil\n # Conversions: the checked form panics \"arithmetic overflow\" when the\n # value does not fit; the `?` form answers nil instead; the unchecked\n # form is the instruction - truncated, or extended by the sign of the\n # type it leaves - as the prelude's `unsafe_to_u8` is. A pair the\n # prelude already declares is not declared twice.\n def to_i8 : Int8\n def to_i8? : Int8 | ::Nil\n def to_i? : Int32 | ::Nil\n def to_s(base : Int32) : String\n # Fits an `Int64`: the prelude's printer.\n def to_s : String\n def to_u : UInt32\n def to_u128 : UInt128\n def to_u128? : UInt128 | ::Nil\n def to_u16 : UInt16\n def to_u16? : UInt16 | ::Nil\n def to_u32 : UInt32\n def to_u32? : UInt32 | ::Nil\n def to_u64 : UInt64\n def to_u64? : UInt64 | ::Nil\n def to_u8 : UInt8\n def to_u8? : UInt8 | ::Nil\n def to_u? : UInt32 | ::Nil\n def trailing_zeros_count : Int32\n def unsafe_chr : Char\n def unsafe_div(other : Int8) : self\n def unsafe_div(other : Int16) : self\n def unsafe_div(other : Int32) : self\n def unsafe_div(other : Int64) : self\n def unsafe_div(other : Int128) : self\n def unsafe_div(other : UInt8) : self\n def unsafe_div(other : UInt16) : self\n def unsafe_div(other : UInt32) : self\n def unsafe_div(other : UInt64) : self\n def unsafe_div(other : UInt128) : self\n def unsafe_mod(other : Int8) : self\n def unsafe_mod(other : Int16) : self\n def unsafe_mod(other : Int32) : self\n def unsafe_mod(other : Int64) : self\n def unsafe_mod(other : Int128) : self\n def unsafe_mod(other : UInt8) : self\n def unsafe_mod(other : UInt16) : self\n def unsafe_mod(other : UInt32) : self\n def unsafe_mod(other : UInt64) : self\n def unsafe_mod(other : UInt128) : self\n def unsafe_shl(other : Int8) : self\n def unsafe_shl(other : Int16) : self\n def unsafe_shl(other : Int32) : self\n def unsafe_shl(other : Int64) : self\n def unsafe_shl(other : Int128) : self\n def unsafe_shl(other : UInt8) : self\n def unsafe_shl(other : UInt16) : self\n def unsafe_shl(other : UInt32) : self\n def unsafe_shl(other : UInt64) : self\n def unsafe_shl(other : UInt128) : self\n def unsafe_shr(other : Int8) : self\n def unsafe_shr(other : Int16) : self\n def unsafe_shr(other : Int32) : self\n def unsafe_shr(other : Int64) : self\n def unsafe_shr(other : Int128) : self\n def unsafe_shr(other : UInt8) : self\n def unsafe_shr(other : UInt16) : self\n def unsafe_shr(other : UInt32) : self\n def unsafe_shr(other : UInt64) : self\n def unsafe_shr(other : UInt128) : self\n def unsafe_to_i : Int32\n def unsafe_to_i128 : Int128\n def unsafe_to_i16 : Int16\n def unsafe_to_i32 : Int32\n def unsafe_to_i64 : Int64\n def unsafe_to_i8 : Int8\n def unsafe_to_u : UInt32\n def unsafe_to_u128 : UInt128\n def unsafe_to_u16 : UInt16\n def unsafe_to_u32 : UInt32\n def unsafe_to_u64 : UInt64\n def unsafe_to_u8 : UInt8\n def |(other : Int8) : self\n def |(other : Int16) : self\n def |(other : Int32) : self\n def |(other : Int64) : self\n def |(other : Int128) : self\n def |(other : UInt8) : self\n def |(other : UInt16) : self\n def |(other : UInt32) : self\n def |(other : UInt64) : self\n def |(other : UInt128) : self\n def ~ : UInt16\nend\n\nstruct ::UInt32\n def !=(other : Int8) : Bool\n def !=(other : Int16) : Bool\n def !=(other : Int32) : Bool\n def !=(other : Int64) : Bool\n def !=(other : Int128) : Bool\n def !=(other : UInt8) : Bool\n def !=(other : UInt16) : Bool\n def !=(other : UInt32) : Bool\n def !=(other : UInt64) : Bool\n def !=(other : UInt128) : Bool\n def !=(other : Float64) : Bool\n def %(other : UInt32) : UInt32\n def &(other : Int8) : self\n def &(other : Int16) : self\n def &(other : Int32) : self\n def &(other : Int64) : self\n def &(other : Int128) : self\n def &(other : UInt8) : self\n def &(other : UInt16) : self\n def &(other : UInt32) : self\n def &(other : UInt64) : self\n def &(other : UInt128) : self\n def &*(other : Int8) : self\n def &*(other : Int16) : self\n def &*(other : Int32) : self\n def &*(other : Int64) : self\n def &*(other : Int128) : self\n def &*(other : UInt8) : self\n def &*(other : UInt16) : self\n def &*(other : UInt32) : self\n def &*(other : UInt64) : self\n def &*(other : UInt128) : self\n def &+(other : Int8) : self\n def &+(other : Int16) : self\n def &+(other : Int32) : self\n def &+(other : Int64) : self\n def &+(other : Int128) : self\n def &+(other : UInt8) : self\n def &+(other : UInt16) : self\n def &+(other : UInt32) : self\n def &+(other : UInt64) : self\n def &+(other : UInt128) : self\n def &-(other : Int8) : self\n def &-(other : Int16) : self\n def &-(other : Int32) : self\n def &-(other : Int64) : self\n def &-(other : Int128) : self\n def &-(other : UInt8) : self\n def &-(other : UInt16) : self\n def &-(other : UInt32) : self\n def &-(other : UInt64) : self\n def &-(other : UInt128) : self\n def *(other : Int8) : self\n def *(other : Int16) : self\n def *(other : Int32) : self\n def *(other : Int64) : self\n def *(other : Int128) : self\n def *(other : UInt8) : self\n def *(other : UInt16) : self\n def *(other : UInt32) : self\n def *(other : UInt64) : self\n def *(other : UInt128) : self\n def *(other : Float64) : Float64\n def +(other : Int8) : self\n def +(other : Int16) : self\n def +(other : Int32) : self\n def +(other : Int64) : self\n def +(other : Int128) : self\n def +(other : UInt8) : self\n def +(other : UInt16) : self\n def +(other : UInt32) : self\n def +(other : UInt64) : self\n def +(other : UInt128) : self\n def +(other : Float64) : Float64\n def -(other : Int8) : self\n def -(other : Int16) : self\n def -(other : Int32) : self\n def -(other : Int64) : self\n def -(other : Int128) : self\n def -(other : UInt8) : self\n def -(other : UInt16) : self\n def -(other : UInt32) : self\n def -(other : UInt64) : self\n def -(other : UInt128) : self\n def -(other : Float64) : Float64\n # An integer divided by a float is a float, as the prelude has it for\n # `Int32` and `Int64` - and for `UInt8` and `UInt64` too, which the\n # prelude does not write: `x / 3.0` did not type for them, and with a\n # literal divisor the `Float32` overload took it, so `1_u8 / 3.0` was a\n # single, 0.33333334.\n def /(other : Float64) : Float64\n # The zero is checked here rather than left to the processor, which\n # reports an integer division by zero as a floating-point fault.\n def //(other : UInt32) : UInt32\n def <(other : Int8) : Bool\n def <(other : Int16) : Bool\n def <(other : Int32) : Bool\n def <(other : Int64) : Bool\n def <(other : Int128) : Bool\n def <(other : UInt8) : Bool\n def <(other : UInt16) : Bool\n def <(other : UInt32) : Bool\n def <(other : UInt64) : Bool\n def <(other : UInt128) : Bool\n def <(other : Float64) : Bool\n # A shift by the width or more answers zero (or the sign, shifting a\n # negative value right), which the processor does not promise, and a\n # negative count shifts the other way, as the prelude's `Int32` does.\n def <<(count : Int32) : UInt32\n def <=(other : Int8) : Bool\n def <=(other : Int16) : Bool\n def <=(other : Int32) : Bool\n def <=(other : Int64) : Bool\n def <=(other : Int128) : Bool\n def <=(other : UInt8) : Bool\n def <=(other : UInt16) : Bool\n def <=(other : UInt32) : Bool\n def <=(other : UInt64) : Bool\n def <=(other : UInt128) : Bool\n def <=(other : Float64) : Bool\n # Exact for every width: the double is placed against the type's\n # edges, then its floor - one of the type, once inside them - against\n # the value, then its fraction decides a tie. NaN is after every\n # number, as `Float64#<=>` places it. The prelude's `Int64 == Float64`\n # is exact too, and agrees with this on every number.\n def <=>(other : Float64) : Int32\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int64) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int128) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt64) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt128) : Bool\n # Every value of the type is a double exactly, so the double's own\n # comparison is the exact one.\n def ==(other : Float64) : Bool\n def >(other : Int8) : Bool\n def >(other : Int16) : Bool\n def >(other : Int32) : Bool\n def >(other : Int64) : Bool\n def >(other : Int128) : Bool\n def >(other : UInt8) : Bool\n def >(other : UInt16) : Bool\n def >(other : UInt32) : Bool\n def >(other : UInt64) : Bool\n def >(other : UInt128) : Bool\n def >(other : Float64) : Bool\n def >=(other : Int8) : Bool\n def >=(other : Int16) : Bool\n def >=(other : Int32) : Bool\n def >=(other : Int64) : Bool\n def >=(other : Int128) : Bool\n def >=(other : UInt8) : Bool\n def >=(other : UInt16) : Bool\n def >=(other : UInt32) : Bool\n def >=(other : UInt64) : Bool\n def >=(other : UInt128) : Bool\n def >=(other : Float64) : Bool\n def >>(count : Int32) : UInt32\n def ^(other : Int8) : self\n def ^(other : Int16) : self\n def ^(other : Int32) : self\n def ^(other : Int64) : self\n def ^(other : Int128) : self\n def ^(other : UInt8) : self\n def ^(other : UInt16) : self\n def ^(other : UInt32) : self\n def ^(other : UInt64) : self\n def ^(other : UInt128) : self\n def abs : UInt32\n # Bits needed to write the value, the sign excluded: `255.bit_length`\n # is 8, `(-1).bit_length` is 0 and `(-2).bit_length` is 1.\n def bit_length : Int32\n def hash : Int32\n def leading_zeros_count : Int32\n # --- the bits, counted with a logical shift for every type ----------\n # A negative value has its sign bits counted: `(-1).popcount` is 32,\n # one loop of the type's width.\n def popcount : Int32\n def to_f : Float64\n def to_f64 : Float64\n def to_i : Int32\n def to_i128 : Int128\n def to_i128? : Int128 | ::Nil\n def to_i16 : Int16\n def to_i16? : Int16 | ::Nil\n def to_i32 : Int32\n def to_i32? : Int32 | ::Nil\n def to_i64 : Int64\n def to_i64? : Int64 | ::Nil\n # Conversions: the checked form panics \"arithmetic overflow\" when the\n # value does not fit; the `?` form answers nil instead; the unchecked\n # form is the instruction - truncated, or extended by the sign of the\n # type it leaves - as the prelude's `unsafe_to_u8` is. A pair the\n # prelude already declares is not declared twice.\n def to_i8 : Int8\n def to_i8? : Int8 | ::Nil\n def to_i? : Int32 | ::Nil\n def to_s(base : Int32) : String\n # Fits an `Int64`: the prelude's printer.\n def to_s : String\n def to_u : UInt32\n def to_u128 : UInt128\n def to_u128? : UInt128 | ::Nil\n def to_u16 : UInt16\n def to_u16? : UInt16 | ::Nil\n def to_u32 : UInt32\n def to_u32? : UInt32 | ::Nil\n def to_u64 : UInt64\n def to_u64? : UInt64 | ::Nil\n def to_u8 : UInt8\n def to_u8? : UInt8 | ::Nil\n def to_u? : UInt32 | ::Nil\n def trailing_zeros_count : Int32\n def unsafe_chr : Char\n def unsafe_div(other : Int8) : self\n def unsafe_div(other : Int16) : self\n def unsafe_div(other : Int32) : self\n def unsafe_div(other : Int64) : self\n def unsafe_div(other : Int128) : self\n def unsafe_div(other : UInt8) : self\n def unsafe_div(other : UInt16) : self\n def unsafe_div(other : UInt32) : self\n def unsafe_div(other : UInt64) : self\n def unsafe_div(other : UInt128) : self\n def unsafe_mod(other : Int8) : self\n def unsafe_mod(other : Int16) : self\n def unsafe_mod(other : Int32) : self\n def unsafe_mod(other : Int64) : self\n def unsafe_mod(other : Int128) : self\n def unsafe_mod(other : UInt8) : self\n def unsafe_mod(other : UInt16) : self\n def unsafe_mod(other : UInt32) : self\n def unsafe_mod(other : UInt64) : self\n def unsafe_mod(other : UInt128) : self\n def unsafe_shl(other : Int8) : self\n def unsafe_shl(other : Int16) : self\n def unsafe_shl(other : Int32) : self\n def unsafe_shl(other : Int64) : self\n def unsafe_shl(other : Int128) : self\n def unsafe_shl(other : UInt8) : self\n def unsafe_shl(other : UInt16) : self\n def unsafe_shl(other : UInt32) : self\n def unsafe_shl(other : UInt64) : self\n def unsafe_shl(other : UInt128) : self\n def unsafe_shr(other : Int8) : self\n def unsafe_shr(other : Int16) : self\n def unsafe_shr(other : Int32) : self\n def unsafe_shr(other : Int64) : self\n def unsafe_shr(other : Int128) : self\n def unsafe_shr(other : UInt8) : self\n def unsafe_shr(other : UInt16) : self\n def unsafe_shr(other : UInt32) : self\n def unsafe_shr(other : UInt64) : self\n def unsafe_shr(other : UInt128) : self\n def unsafe_to_i : Int32\n def unsafe_to_i128 : Int128\n def unsafe_to_i16 : Int16\n def unsafe_to_i32 : Int32\n def unsafe_to_i64 : Int64\n def unsafe_to_i8 : Int8\n def unsafe_to_u : UInt32\n def unsafe_to_u128 : UInt128\n def unsafe_to_u16 : UInt16\n def unsafe_to_u32 : UInt32\n def unsafe_to_u64 : UInt64\n def unsafe_to_u8 : UInt8\n def |(other : Int8) : self\n def |(other : Int16) : self\n def |(other : Int32) : self\n def |(other : Int64) : self\n def |(other : Int128) : self\n def |(other : UInt8) : self\n def |(other : UInt16) : self\n def |(other : UInt32) : self\n def |(other : UInt64) : self\n def |(other : UInt128) : self\n def ~ : UInt32\nend\n\nstruct ::UInt64\n def !=(other : Int8) : Bool\n def !=(other : Int16) : Bool\n def !=(other : Int128) : Bool\n def !=(other : UInt32) : Bool\n def !=(other : UInt128) : Bool\n def &(other : Int8) : self\n def &(other : Int16) : self\n def &(other : Int128) : self\n def &(other : UInt16) : self\n def &(other : UInt32) : self\n def &(other : UInt128) : self\n def &*(other : Int8) : self\n def &*(other : Int16) : self\n def &*(other : Int128) : self\n def &*(other : UInt16) : self\n def &*(other : UInt32) : self\n def &*(other : UInt128) : self\n def &+(other : Int8) : self\n def &+(other : Int16) : self\n def &+(other : Int128) : self\n def &+(other : UInt16) : self\n def &+(other : UInt32) : self\n def &+(other : UInt128) : self\n def &-(other : Int8) : self\n def &-(other : Int16) : self\n def &-(other : Int128) : self\n def &-(other : UInt16) : self\n def &-(other : UInt32) : self\n def &-(other : UInt128) : self\n def *(other : Int8) : self\n def *(other : Int16) : self\n def *(other : Int128) : self\n def *(other : UInt16) : self\n def *(other : UInt32) : self\n def *(other : UInt128) : self\n def +(other : Int8) : self\n def +(other : Int16) : self\n def +(other : Int128) : self\n def +(other : UInt16) : self\n def +(other : UInt32) : self\n def +(other : UInt128) : self\n def -(other : Int8) : self\n def -(other : Int16) : self\n def -(other : Int128) : self\n def -(other : UInt16) : self\n def -(other : UInt32) : self\n def -(other : UInt128) : self\n # An integer divided by a float is a float, as the prelude has it for\n # `Int32` and `Int64` - and for `UInt8` and `UInt64` too, which the\n # prelude does not write: `x / 3.0` did not type for them, and with a\n # literal divisor the `Float32` overload took it, so `1_u8 / 3.0` was a\n # single, 0.33333334.\n def /(other : Float64) : Float64\n def <(other : Int8) : Bool\n def <(other : Int16) : Bool\n def <(other : Int128) : Bool\n def <(other : UInt32) : Bool\n def <(other : UInt128) : Bool\n # A shift by the width or more answers zero (or the sign, shifting a\n # negative value right), which the processor does not promise, and a\n # negative count shifts the other way, as the prelude's `Int32` does.\n def <<(count : Int32) : UInt64\n def <=(other : Int8) : Bool\n def <=(other : Int16) : Bool\n def <=(other : Int128) : Bool\n def <=(other : UInt32) : Bool\n def <=(other : UInt128) : Bool\n # Exact for every width: the double is placed against the type's\n # edges, then its floor - one of the type, once inside them - against\n # the value, then its fraction decides a tie. NaN is after every\n # number, as `Float64#<=>` places it. The prelude's `Int64 == Float64`\n # is exact too, and agrees with this on every number.\n def <=>(other : Float64) : Int32\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int128) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt128) : Bool\n def >(other : Int8) : Bool\n def >(other : Int16) : Bool\n def >(other : Int128) : Bool\n def >(other : UInt32) : Bool\n def >(other : UInt128) : Bool\n def >=(other : Int8) : Bool\n def >=(other : Int16) : Bool\n def >=(other : Int128) : Bool\n def >=(other : UInt32) : Bool\n def >=(other : UInt128) : Bool\n def >>(count : Int32) : UInt64\n def ^(other : Int8) : self\n def ^(other : Int16) : self\n def ^(other : Int128) : self\n def ^(other : UInt16) : self\n def ^(other : UInt32) : self\n def ^(other : UInt128) : self\n def abs : UInt64\n # Bits needed to write the value, the sign excluded: `255.bit_length`\n # is 8, `(-1).bit_length` is 0 and `(-2).bit_length` is 1.\n def bit_length : Int32\n def leading_zeros_count : Int32\n # --- the bits, counted with a logical shift for every type ----------\n # A negative value has its sign bits counted: `(-1).popcount` is 32,\n # one loop of the type's width.\n def popcount : Int32\n def to_i128 : Int128\n def to_i128? : Int128 | ::Nil\n def to_i16 : Int16\n def to_i16? : Int16 | ::Nil\n def to_i32? : Int32 | ::Nil\n def to_i64? : Int64 | ::Nil\n # Conversions: the checked form panics \"arithmetic overflow\" when the\n # value does not fit; the `?` form answers nil instead; the unchecked\n # form is the instruction - truncated, or extended by the sign of the\n # type it leaves - as the prelude's `unsafe_to_u8` is. A pair the\n # prelude already declares is not declared twice.\n def to_i8 : Int8\n def to_i8? : Int8 | ::Nil\n def to_i? : Int32 | ::Nil\n def to_u : UInt32\n def to_u128 : UInt128\n def to_u128? : UInt128 | ::Nil\n def to_u16 : UInt16\n def to_u16? : UInt16 | ::Nil\n def to_u32 : UInt32\n def to_u32? : UInt32 | ::Nil\n def to_u64? : UInt64 | ::Nil\n def to_u8? : UInt8 | ::Nil\n def to_u? : UInt32 | ::Nil\n def trailing_zeros_count : Int32\n def unsafe_div(other : Int8) : self\n def unsafe_div(other : Int16) : self\n def unsafe_div(other : Int128) : self\n def unsafe_div(other : UInt16) : self\n def unsafe_div(other : UInt32) : self\n def unsafe_div(other : UInt128) : self\n def unsafe_mod(other : Int8) : self\n def unsafe_mod(other : Int16) : self\n def unsafe_mod(other : Int128) : self\n def unsafe_mod(other : UInt16) : self\n def unsafe_mod(other : UInt32) : self\n def unsafe_mod(other : UInt128) : self\n def unsafe_shl(other : Int8) : self\n def unsafe_shl(other : Int16) : self\n def unsafe_shl(other : Int128) : self\n def unsafe_shl(other : UInt16) : self\n def unsafe_shl(other : UInt32) : self\n def unsafe_shl(other : UInt128) : self\n def unsafe_shr(other : Int8) : self\n def unsafe_shr(other : Int16) : self\n def unsafe_shr(other : Int128) : self\n def unsafe_shr(other : UInt16) : self\n def unsafe_shr(other : UInt32) : self\n def unsafe_shr(other : UInt128) : self\n def unsafe_to_i128 : Int128\n def unsafe_to_i16 : Int16\n def unsafe_to_i8 : Int8\n def unsafe_to_u : UInt32\n def unsafe_to_u128 : UInt128\n def unsafe_to_u16 : UInt16\n def unsafe_to_u32 : UInt32\n def |(other : Int8) : self\n def |(other : Int16) : self\n def |(other : Int128) : self\n def |(other : UInt16) : self\n def |(other : UInt32) : self\n def |(other : UInt128) : self\n def ~ : UInt64\nend\n\nstruct ::UInt8\n def !=(other : Int8) : Bool\n def !=(other : Int16) : Bool\n def !=(other : Int128) : Bool\n def !=(other : UInt32) : Bool\n def !=(other : UInt128) : Bool\n def %(other : UInt8) : UInt8\n def &(other : Int8) : self\n def &(other : Int16) : self\n def &(other : Int128) : self\n def &(other : UInt16) : self\n def &(other : UInt32) : self\n def &(other : UInt128) : self\n def &*(other : Int8) : self\n def &*(other : Int16) : self\n def &*(other : Int128) : self\n def &*(other : UInt16) : self\n def &*(other : UInt32) : self\n def &*(other : UInt128) : self\n def &+(other : Int8) : self\n def &+(other : Int16) : self\n def &+(other : Int128) : self\n def &+(other : UInt16) : self\n def &+(other : UInt32) : self\n def &+(other : UInt128) : self\n def &-(other : Int8) : self\n def &-(other : Int16) : self\n def &-(other : Int128) : self\n def &-(other : UInt16) : self\n def &-(other : UInt32) : self\n def &-(other : UInt128) : self\n def *(other : Int8) : self\n def *(other : Int16) : self\n def *(other : Int128) : self\n def *(other : UInt16) : self\n def *(other : UInt32) : self\n def *(other : UInt128) : self\n def +(other : Int8) : self\n def +(other : Int16) : self\n def +(other : Int128) : self\n def +(other : UInt16) : self\n def +(other : UInt32) : self\n def +(other : UInt128) : self\n def -(other : Int8) : self\n def -(other : Int16) : self\n def -(other : Int128) : self\n def -(other : UInt16) : self\n def -(other : UInt32) : self\n def -(other : UInt128) : self\n # An integer divided by a float is a float, as the prelude has it for\n # `Int32` and `Int64` - and for `UInt8` and `UInt64` too, which the\n # prelude does not write: `x / 3.0` did not type for them, and with a\n # literal divisor the `Float32` overload took it, so `1_u8 / 3.0` was a\n # single, 0.33333334.\n def /(other : Float64) : Float64\n # The zero is checked here rather than left to the processor, which\n # reports an integer division by zero as a floating-point fault.\n def //(other : UInt8) : UInt8\n def <(other : Int8) : Bool\n def <(other : Int16) : Bool\n def <(other : Int128) : Bool\n def <(other : UInt32) : Bool\n def <(other : UInt128) : Bool\n # A shift by the width or more answers zero (or the sign, shifting a\n # negative value right), which the processor does not promise, and a\n # negative count shifts the other way, as the prelude's `Int32` does.\n def <<(count : Int32) : UInt8\n def <=(other : Int8) : Bool\n def <=(other : Int16) : Bool\n def <=(other : Int128) : Bool\n def <=(other : UInt32) : Bool\n def <=(other : UInt128) : Bool\n # Exact for every width: the double is placed against the type's\n # edges, then its floor - one of the type, once inside them - against\n # the value, then its fraction decides a tie. NaN is after every\n # number, as `Float64#<=>` places it. The prelude's `Int64 == Float64`\n # is exact too, and agrees with this on every number.\n def <=>(other : Float64) : Int32\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int8) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int16) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : Int128) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt32) : Bool\n # The prelude compares six types and adds five: `UInt16` is in its\n # comparison cross (a wide character is compared against a\n # separator on every Windows path) and not in its arithmetic one,\n # so the comparisons for a pair it already declares are skipped\n # here and the arithmetic below is not. Redefining a prelude\n # method is refused by name (SPEC.md R-3), which is how the split\n # announced itself.\n def ==(other : UInt128) : Bool\n def >(other : Int8) : Bool\n def >(other : Int16) : Bool\n def >(other : Int128) : Bool\n def >(other : UInt32) : Bool\n def >(other : UInt128) : Bool\n def >=(other : Int8) : Bool\n def >=(other : Int16) : Bool\n def >=(other : Int128) : Bool\n def >=(other : UInt32) : Bool\n def >=(other : UInt128) : Bool\n def >>(count : Int32) : UInt8\n def ^(other : Int8) : self\n def ^(other : Int16) : self\n def ^(other : Int128) : self\n def ^(other : UInt16) : self\n def ^(other : UInt32) : self\n def ^(other : UInt128) : self\n def abs : UInt8\n # Bits needed to write the value, the sign excluded: `255.bit_length`\n # is 8, `(-1).bit_length` is 0 and `(-2).bit_length` is 1.\n def bit_length : Int32\n def leading_zeros_count : Int32\n # --- the bits, counted with a logical shift for every type ----------\n # A negative value has its sign bits counted: `(-1).popcount` is 32,\n # one loop of the type's width.\n def popcount : Int32\n def to_i128 : Int128\n def to_i128? : Int128 | ::Nil\n def to_i16 : Int16\n def to_i16? : Int16 | ::Nil\n def to_i32? : Int32 | ::Nil\n def to_i64? : Int64 | ::Nil\n # Conversions: the checked form panics \"arithmetic overflow\" when the\n # value does not fit; the `?` form answers nil instead; the unchecked\n # form is the instruction - truncated, or extended by the sign of the\n # type it leaves - as the prelude's `unsafe_to_u8` is. A pair the\n # prelude already declares is not declared twice.\n def to_i8 : Int8\n def to_i8? : Int8 | ::Nil\n def to_i? : Int32 | ::Nil\n def to_u : UInt32\n def to_u128 : UInt128\n def to_u128? : UInt128 | ::Nil\n def to_u16 : UInt16\n def to_u16? : UInt16 | ::Nil\n def to_u32 : UInt32\n def to_u32? : UInt32 | ::Nil\n def to_u64? : UInt64 | ::Nil\n def to_u8? : UInt8 | ::Nil\n def to_u? : UInt32 | ::Nil\n def trailing_zeros_count : Int32\n def unsafe_div(other : Int8) : self\n def unsafe_div(other : Int16) : self\n def unsafe_div(other : Int128) : self\n def unsafe_div(other : UInt16) : self\n def unsafe_div(other : UInt32) : self\n def unsafe_div(other : UInt128) : self\n def unsafe_mod(other : Int8) : self\n def unsafe_mod(other : Int16) : self\n def unsafe_mod(other : Int128) : self\n def unsafe_mod(other : UInt16) : self\n def unsafe_mod(other : UInt32) : self\n def unsafe_mod(other : UInt128) : self\n def unsafe_shl(other : Int8) : self\n def unsafe_shl(other : Int16) : self\n def unsafe_shl(other : Int128) : self\n def unsafe_shl(other : UInt16) : self\n def unsafe_shl(other : UInt32) : self\n def unsafe_shl(other : UInt128) : self\n def unsafe_shr(other : Int8) : self\n def unsafe_shr(other : Int16) : self\n def unsafe_shr(other : Int128) : self\n def unsafe_shr(other : UInt16) : self\n def unsafe_shr(other : UInt32) : self\n def unsafe_shr(other : UInt128) : self\n def unsafe_to_i128 : Int128\n def unsafe_to_i16 : Int16\n def unsafe_to_i8 : Int8\n def unsafe_to_u : UInt32\n def unsafe_to_u128 : UInt128\n def unsafe_to_u16 : UInt16\n def unsafe_to_u32 : UInt32\n def |(other : Int8) : self\n def |(other : Int16) : self\n def |(other : Int128) : self\n def |(other : UInt16) : self\n def |(other : UInt32) : self\n def |(other : UInt128) : self\n def ~ : UInt8\nend\n" }, { "name": "std/io", "path": "src/std/io.iyi", - "sourceSha256": "010cf487070cc712605ec215b8d2768039b051b3de353789f35434529a143d53", + "sourceSha256": "1affb77bc71581778fd307af019a74f26770ea04c73b482fb74b1f0a2186051e", "surface": "module std/io\n\npub struct ByteFormat\n struct BigEndian\n def self.decode_int32(buffer : Pointer(UInt8)) : Int32\n def self.decode_int32(io : IyiIO) : Int32\n def self.decode_int64(buffer : Pointer(UInt8)) : Int64\n def self.decode_int64(io : IyiIO) : Int64\n def self.decode_uint64(buffer : Pointer(UInt8)) : UInt64\n def self.decode_uint64(io : IyiIO) : UInt64\n def self.decode_uint8(buffer : Pointer(UInt8)) : UInt8\n def self.decode_uint8(io : IyiIO) : UInt8\n def self.encode(value : Int32, buffer : Pointer(UInt8)) : Nil\n def self.encode(value : Int64, buffer : Pointer(UInt8)) : Nil\n def self.encode(value : UInt64, buffer : Pointer(UInt8)) : Nil\n def self.encode(value : UInt8, buffer : Pointer(UInt8)) : Nil\n def self.encode(value : Int32, io : IyiIO) : Nil\n def self.encode(value : Int64, io : IyiIO) : Nil\n def self.encode(value : UInt64, io : IyiIO) : Nil\n def self.encode(value : UInt8, io : IyiIO) : Nil\n def initialize\n end\n # Two's complement by arithmetic, so a negative value never meets an\n # unsigned conversion: the bit pattern of a signed integer as a UInt64\n # and back.\n struct Bits\n def self.get_big(buffer : Pointer(UInt8), count : Int32) : UInt64\n def self.get_little(buffer : Pointer(UInt8), count : Int32) : UInt64\n def initialize\n def self.of_int32(value : Int32) : UInt64\n def self.of_int64(value : Int64) : UInt64\n def self.put_big(bits : UInt64, buffer : Pointer(UInt8), count : Int32) : Nil\n # The low `count` bytes of `bits`, least significant first.\n def self.put_little(bits : UInt64, buffer : Pointer(UInt8), count : Int32) : Nil\n # `count` bytes out of `io`, or a refusal naming the shortfall.\n def self.take(io : IyiIO, count : Int32) : String\n def self.to_int32(bits : UInt64) : Int32\n def self.to_int64(bits : UInt64) : Int64\n end\n struct LittleEndian\n def self.decode_int32(buffer : Pointer(UInt8)) : Int32\n def self.decode_int32(io : IyiIO) : Int32\n def self.decode_int64(buffer : Pointer(UInt8)) : Int64\n def self.decode_int64(io : IyiIO) : Int64\n def self.decode_uint64(buffer : Pointer(UInt8)) : UInt64\n def self.decode_uint64(io : IyiIO) : UInt64\n def self.decode_uint8(buffer : Pointer(UInt8)) : UInt8\n def self.decode_uint8(io : IyiIO) : UInt8\n def self.encode(value : Int32, buffer : Pointer(UInt8)) : Nil\n def self.encode(value : Int64, buffer : Pointer(UInt8)) : Nil\n def self.encode(value : UInt64, buffer : Pointer(UInt8)) : Nil\n def self.encode(value : UInt8, buffer : Pointer(UInt8)) : Nil\n def self.encode(value : Int32, io : IyiIO) : Nil\n def self.encode(value : Int64, io : IyiIO) : Nil\n def self.encode(value : UInt64, io : IyiIO) : Nil\n def self.encode(value : UInt8, io : IyiIO) : Nil\n def initialize\n end\n alias NetworkEndian = BigEndian\n alias SystemEndian = LittleEndian\n def initialize\nend\n\n# Another stream up to a delimiter. The delimiter is consumed and not\n# answered; the inner stream is left just past it. Without a delimiter\n# the whole rest of the stream is answered.\npub class Delimited < Reader\n def delimiter : String\n # Exact, as the prelude's is: the inner stream is read until a content\n # byte is held or the delimiter (or the end) is met. Answering from the\n # inner stream alone said \"not yet\" with nothing but the delimiter left,\n # and the next read answered nil.\n def eof? : Bool\n def initialize(io : IyiIO, delimiter : String)\n def read_byte : UInt8 | ::Nil\n def write(bytes : Pointer(UInt8), count : Int32) : Nil\nend\n\n# Bytes printed sixteen to a line, `hexdump -C` style, on an `IyiIO`.\npub class Hexdump\n def dump(bytes : Pointer(UInt8), count : Int32) : Nil\n def dump(text : String) : Nil\n # Everything left in `source`.\n def dump(source : IyiIO) : Nil\n def initialize(out __arg0 : IyiIO)\nend\n\n# An `IyiIO` over bytes in memory. Writes land at the cursor and grow the\n# buffer; reads advance the cursor; `to_s` is the whole buffer.\npub class Memory < ::IyiIO\n # Forgets every byte; the cursor returns to the start.\n def clear : Nil\n def close : Nil\n def closed? : Bool\n def empty? : Bool\n def eof? : Bool\n def flush : Nil\n def initialize(capacity : Int32 = 64)\n # A buffer holding a copy of `text`'s bytes, the cursor at its start.\n def initialize(text : String)\n # The cursor, in bytes from the start. Int64, as a file's position is:\n # with `std/file` imported every `IyiIO` has a `pos : Int64`, and a\n # narrower answer here made `pos` on any `IyiIO` fail to compile.\n def pos : Int64\n def pos=(position : Int32) : Nil\n # The cursor moved through an `IyiIO`: the stream's own position, not\n # the descriptor's that `std/file` would move.\n def pos=(position : Int64) : Int64\n def read_all : String\n def read_byte : UInt8 | ::Nil\n def read_bytes(count : Int32) : String\n def read_line(chomp : Bool = false) : String | ::Nil\n def rewind : Nil\n # Bytes held, written or given.\n def size : Int32\n def to_s : String\n def write(bytes : Pointer(UInt8), count : Int32) : Nil\nend\n\n# One stream written to several. `close` closes this writer, not the\n# streams behind it.\npub class MultiWriter < ::IyiIO\n def close : Nil\n def closed? : Bool\n def eof? : Bool\n def flush : Nil\n def initialize(writers : Array(IyiIO))\n def read_all : String\n def read_byte : UInt8 | ::Nil\n def read_bytes(count : Int32) : String\n def read_line(chomp : Bool = false) : String | ::Nil\n def write(bytes : Pointer(UInt8), count : Int32) : Nil\nend\n\n# The reading end of `IyiIO.pipe`.\npub class PipeReader < ::IyiIO\n def initialize(channel : Channel(String))\nend\n\n# The writing end of `IyiIO.pipe`: every write is sent at once.\npub class PipeWriter < ::IyiIO\n def initialize(channel : Channel(String))\nend\n\n# At most `limit` bytes of another stream.\npub class Sized < Reader\n def eof? : Bool\n def initialize(io : IyiIO, limit : Int32)\n def read_all : String\n def read_byte : UInt8 | ::Nil\n # The inner stream is asked for at most 64 KB at a time: a descriptor\n # stream's `read_bytes` allocates the whole count before it reads, and\n # `read_all` under a limit of Int32::MAX took 2 GB to read two bytes.\n def read_bytes(count : Int32) : String\n # Bytes the limit still allows, whether or not the stream has them.\n def remaining : Int32\n def write(bytes : Pointer(UInt8), count : Int32) : Nil\nend\n\nclass ::IyiIO\n def <<(value) : self\n # Everything left in `source` (at most `limit` bytes) written to\n # `target`, which is flushed; answers the number of bytes moved.\n def self.copy(source : IyiIO, target : IyiIO, limit : Int32 | ::Nil = nil) : Int64\n def each_line(chomp : Bool = false, &block : (String -> Nil)) : Nil\n # Every remaining line, `chomp` dropping the line ends.\n def lines(chomp : Bool = false) : Array(String)\n # The two ends of a pipe between tasks: what is written to the writer is\n # read from the reader, in order.\n #\n # reader, writer = IyiIO.pipe\n # group do |g|\n # g.spawn { IyiIO.copy(file, writer); writer.close; 0 }\n # g.spawn { IyiIO.copy(reader, socket.io); 0 }\n # end\n #\n # Each write is delivered at once, as one piece, and a write waits while\n # *capacity* pieces are unread; a read waits for one. Closing the writer\n # ends the reader after the last piece; closing the reader refuses the\n # next write, as a pipe whose reader is gone does. The pipe is its\n # thread's, for the tasks on the thread that made it.\n def self.pipe(capacity : Int32 = 16) : ::Tuple(Std::Io::PipeReader, Std::Io::PipeWriter)\n # The next byte, or nil at the end.\n def read_byte : UInt8 | ::Nil\n # The next UTF-8 code point, or nil at the end. A lead byte that is not\n # one (a continuation byte, C0/C1, F5 and above), a continuation byte that\n # is not one, or a sequence the stream ends inside is refused. The first\n # continuation byte is held to the range its lead allows (Unicode Table\n # 3-7), so an overlong form, a surrogate and a code point past U+10FFFF\n # are refused at the byte that gives them away, not decoded into a Char\n # outside Unicode.\n def read_char : Char | ::Nil\n def write_byte(byte : UInt8) : Nil\nend\n" }, { @@ -322,7 +322,7 @@ { "name": "std/json", "path": "src/std/json.iyi", - "sourceSha256": "3ee61ca665e8c34ebf067b55952d565a6aaabc6b0282daa11b7901532a319703", + "sourceSha256": "03d79723649d01ee8c83b948fa958b0a765ef8a288f90e292587a1a7e2b7a985", "surface": "module std/json\n\n# `derive serializable`, in a struct or class whose fields are written with\n# their types: the type reads and writes itself as a JSON object, a key per\n# field, named as the field is.\n#\n# pub struct Coord\n# getter x : Float64\n# @[JSON::Field(key: \"label\")]\n# getter name : String\n# derive serializable\n# end\n#\n# Coord.from_json(%({\"x\": 1.5, \"label\": \"a\"})).x # => 1.5\n# coord.to_json # => {\"x\":1.5,\"label\":\"a\"}\n#\n# A field's value is read and written as `JSON.read_value` and\n# `JSON.write_value` read and write its type: the built-in values, an enum,\n# an `Array` or `Hash(String, V)` of them, an `Any`, another serializable\n# type, a union of those (`String | Int64`). A key the type has no field\n# for is passed over; a field with no key keeps its default when it is\n# written with one, as does a field with a default whose key holds `null`\n# where its type does not admit nil; a field with neither is refused by\n# name unless its type admits `nil`, which it then holds.\n# `@[JSON::Field(...)]` renames a field, leaves it out either way, leaves\n# a nil one out, or reads and writes it through a converter (see\n# `JSON::Field`). It is what the other library's `include\n# JSON::Serializable` writes, and the derive runs once, in the type's own\n# module (SPEC.md II.4), so a consumer calls the methods without the macro.\n#\n# Each field's options are read off the type when its methods are compiled,\n# in one `begin` so what a field's annotation says is known to every line\n# after it; and refused there, with the field named, when they do not make\n# sense.\npub macro serializable(declaration)\n\n# One JSON value of any kind, the result of `JSON.parse`. The `as_x` methods\n# panic when the value is of another kind; the `as_x?` ones answer `nil`.\npub struct Any\n alias Type = Nil | Bool | Int64 | Float64 | String | Array(Any) | Hash(String, Any)\n # Equal when the values are: an integer equals the float with the same\n # value, arrays element by element, objects key by key in any order.\n def ==(other : Any) : Bool\n def [](index : Int32) : Any\n def [](key : String) : Any\n def []?(index : Int32) : Any | ::Nil\n def []?(key : String) : Any | ::Nil\n def as_a : Array(Any)\n def as_a? : Array(Any) | ::Nil\n def as_bool : Bool\n def as_bool? : Bool | ::Nil\n # A float, or an integer read as one.\n def as_f : Float64\n def as_f? : Float64 | ::Nil\n def as_h : Hash(String, Any)\n def as_h? : Hash(String, Any) | ::Nil\n def as_i : Int32\n def as_i64 : Int64\n def as_i64? : Int64 | ::Nil\n def as_i? : Int32 | ::Nil\n def as_nil : Nil\n def as_s : String\n def as_s? : String | ::Nil\n def dig(key : String) : Any\n def dig(index : Int32) : Any\n def dig(k : String, *subkeys : String | Int32) : Any\n def dig(idx : Int32, *subkeys : String | Int32) : Any\n def dig?(key : String) : Any | ::Nil\n def dig?(index : Int32) : Any | ::Nil\n def dig?(k : String, *subkeys : String | Int32) : Any | ::Nil\n def dig?(idx : Int32, *subkeys : String | Int32) : Any | ::Nil\n protected def float_value? : Float64 | ::Nil\n # Consistent with `==`: a whole float hashes as the integer it equals, an\n # object's hash does not depend on the order its keys were written in.\n def hash : Int32\n def initialize(raw : Type)\n def inspect : String\n # The kind of value this is, in the word a refusal uses: `null`, `bool`,\n # `integer`, `float`, `string`, `array` or `object`.\n def kind : String\n def self.new(val : Int32) : Any\n def raw : Type\n # How many elements an array or an object holds. A scalar has no size.\n def size : Int32\n def size? : Int32 | ::Nil\n def to_ascii_json : String\n def to_json : String\n def to_pretty_json(indent : String = \" \") : String\n # A string as itself and null as nothing, as `YAML::Any#to_s` and the\n # other library answer them; a number, a boolean or a collection as its\n # JSON text. A string was its JSON text too, so `\"hello #{doc[\"name\"]}\"`\n # printed `hello \"Ada\"`, and null printed `null`.\n def to_s : String\nend\n\n# Writes a document one value at a time. `indent=` makes it pretty;\n# `ascii_only=` escapes every non-ASCII code point.\npub class Builder\n def array(& : (-> Nil)) : Nil\n def ascii_only : Bool\n def ascii_only=(value : Bool) : Bool\n def bool(val : Bool) : Nil\n # Whether no value has been written yet.\n def empty? : Bool\n def end_array : Nil\n def end_object : Nil\n def field(name : String, val : Any) : Nil\n def field(name : String, val : String) : Nil\n def field(name : String, val : Int32) : Nil\n def field(name : String, val : Int64) : Nil\n def field(name : String, val : Float64) : Nil\n def field(name : String, val : Bool) : Nil\n def field(name : String, val : Nil) : Nil\n # A key; the value that follows it is written by the next call. A key\n # whose text is one this object already holds is refused, naming both:\n # the reader refuses a duplicate key, and the builder wrote one - a key\n # given twice, or two Strings whose bytes are not UTF-8 and are both\n # written as U+FFFD (`{\"a\\xFF\" => 1, \"a\\xFE\" => 2}` was written\n # `{\"a\\uFFFD\":1,\"a\\uFFFD\":2}`, and `JSON.parse` refused it).\n def field(name : String) : Nil\n def field(name : String, & : (-> Nil)) : Nil\n def indent=(spaces : Int32) : Nil\n def indent=(str : String) : Nil\n def initialize\n def max_nesting : Int32\n def max_nesting=(value : Int32) : Int32\n def null : Nil\n def number(val : Int32) : Nil\n def number(val : Int64) : Nil\n def number(val : UInt8) : Nil\n def number(val : UInt64) : Nil\n def number(val : Float64) : Nil\n def object(& : (-> Nil)) : Nil\n # Text written as it is: the caller's own JSON.\n def raw(str : String) : Nil\n def start_array : Nil\n def start_object : Nil\n def string(val : String) : Nil\n # The document's text. A container still open or no value written at all\n # is not a document, and is refused.\n def to_s : String\nend\n\npub struct JSON\n # How `derive serializable` writes and reads one field, the other\n # library's `JSON::Field`:\n #\n # @[JSON::Field(key: \"userName\")]\n # getter user_name : String\n #\n # * `key:` the object key, where it is not the field's own name;\n # * `ignore: true` neither written nor read, `ignore_serialize: true` not\n # written, `ignore_deserialize: true` not read - a field that is not\n # read keeps its default, or is nil, and needs one or the other;\n # * `emit_null: false` a nil field left out of the object. A nil field is\n # written as `null` otherwise, where the other library leaves it out\n # unless asked;\n # * `converter:` a type whose `self.to_json(value, json : Builder)` writes\n # the field and `self.from_json(pull : PullParser)` reads it.\n #\n # An option it does not know, a key that is not a string, and two fields\n # written under one key are refused when the type is compiled.\n annotation Field\n end\n # Text written through a `Builder` the block is handed. `indent` is a\n # count of spaces or the string one level is indented by. A block that\n # writes no value, or leaves a container open, is refused.\n def self.build(indent : String | Int32 | Nil = nil, & : (Builder -> Nil)) : String\n # The document read into `type`: `Nil`, `Bool`, `Int32`, `Int64`, `UInt8`,\n # `UInt64`, `Float64`, `String`, `Any`, an enum, a type with `derive\n # serializable`, a union of them as `read_union` reads one, or an `Array`\n # or `Hash(String, V)` of those. An integer outside the type is refused,\n # as is anything after the root value.\n def self.from_json(str : String, type : T.class) : T forall T\n def initialize\n # The document as an `Any`; panics with the line and column of the first\n # thing it refuses.\n def self.parse(input : String) : Any\n # The document as an `Any`, or `nil` when it is not one.\n def self.parse?(input : String) : Any | ::Nil\n def self.read_value(pull : PullParser, type : Nil.class) : Nil\n def self.read_value(pull : PullParser, type : Bool.class) : Bool\n def self.read_value(pull : PullParser, type : Int32.class) : Int32\n def self.read_value(pull : PullParser, type : Int64.class) : Int64\n def self.read_value(pull : PullParser, type : UInt8.class) : UInt8\n # An integer past Int64 reaches the reader as a float that still carries\n # its digits; those are read here, so the whole UInt64 range comes back.\n def self.read_value(pull : PullParser, type : UInt64.class) : UInt64\n def self.read_value(pull : PullParser, type : Float64.class) : Float64\n def self.read_value(pull : PullParser, type : String.class) : String\n def self.read_value(pull : PullParser, type : Any.class) : Any\n def self.read_value(pull : PullParser, type : Array(T).class) : Array(T) forall T\n def self.read_value(pull : PullParser, type : Hash(String, V).class) : Hash(String, V) forall V\n # A type that reads itself: the `initialize(pull)` a `derive\n # serializable` wrote. A type that admits nil reads `null` as the nil and\n # anything else as the other type (`x || raise`, as the derive asks it),\n # the way `write_or_null` writes it. That was `T.new(pull)` too, and\n # `Int32 | Nil` has no such `new`, so `JSON.from_json(\"5\", Int32?)` and\n # `Array(Int32?)` did not build. Any other union is `read_union`'s.\n def self.read_value(pull : PullParser, type : T.class) : T forall T\n # Any value `write_value` knows: `nil`, a `Bool`, a prelude integer, a\n # `Float64`, a `String`, an `Any`, an enum, an `Array` or `Hash` of those,\n # or a type that implements `ToJSON`; a union of them, written as the\n # member it holds is.\n def self.to_json(obj : T) : String forall T\n def self.write_value(val : Nil, b : Builder) : Nil\n def self.write_value(val : Bool, b : Builder) : Nil\n def self.write_value(val : Int32, b : Builder) : Nil\n def self.write_value(val : Int64, b : Builder) : Nil\n def self.write_value(val : UInt8, b : Builder) : Nil\n def self.write_value(val : UInt64, b : Builder) : Nil\n def self.write_value(val : Float64, b : Builder) : Nil\n def self.write_value(val : String, b : Builder) : Nil\n def self.write_value(val : Any, b : Builder) : Nil\n def self.write_value(val : Array(T), b : Builder) : Nil forall T\n # A String key is written as it is, and two of them are never one text.\n def self.write_value(val : Hash(String, V), b : Builder) : Nil forall V\n # Any other key is written by its `to_s`, `nil` as `\"null\"`, the word a\n # JSON null is, so two keys can be one text: `{nil => 1, \"null\" => 2}`\n # was written `{\"null\":1,\"null\":2}`, which `JSON.parse` refuses\n # (\"duplicate key 'null' at line 1, column 11\"). The texts written are\n # kept, and a repeat is refused, naming both keys.\n def self.write_value(val : Hash(K, V), b : Builder) : Nil forall K, V\n # A `ToJSON` value writes itself. An enum member is written as the other\n # library writes one: its name underscored (`Color::DarkRed` is\n # `\"dark_red\"`), and a `@[Flags]` enum as the array of its members'\n # names; a value no member has is written as its number. Told apart here\n # and not by an overload restricted to `Enum`, which a program built\n # from this module's artifact read as a class and could not pass an enum\n # to.\n #\n # A union is matched here whole, `T` the union itself, so\n # `JSON.to_json(1.as(Int32 | String))` called `to_json` on an `Int32`\n # and did not build (issue #135). A member with an overload of its own,\n # an `Int32` or an `Array`, is sent there when the program runs, yet this\n # is still compiled for the whole union, and reached for the rest: an\n # enum, a `ToJSON` type. Each is written as it is alone, by\n # `Object#__json_write` below.\n def self.write_value(val : T, b : Builder) : Nil forall T\nend\n\n# Reads a whole document into an `Any`. Like the lexer it records the first\n# failure instead of panicking; `JSON.parse` and `JSON.parse?` decide what to\n# do with it.\npub class Parser\n def error : String | ::Nil\n def initialize(input : String)\n def parse : Any\nend\n\n# Reads a document one event at a time: `kind` names what the reader is on,\n# the `read_x` methods take it and move on. The grammar is checked as it is\n# read, so a missing comma or colon is refused where it is, with a panic.\npub class PullParser\n enum Kind : Int32\n Null = 0\n Bool = 1\n Int = 2\n Float = 3\n String = 4\n BeginArray = 5\n EndArray = 6\n BeginObject = 7\n EndObject = 8\n EOF = 9\n # Returns `true` if this enum value equals `BeginArray`\n def begin_array? : ::Bool\n # Returns `true` if this enum value equals `BeginObject`\n def begin_object? : ::Bool\n # Returns `true` if this enum value equals `Bool`\n def bool? : ::Bool\n # Returns `true` if this enum value equals `EndArray`\n def end_array? : ::Bool\n # Returns `true` if this enum value equals `EndObject`\n def end_object? : ::Bool\n # Returns `true` if this enum value equals `EOF`\n def eof? : ::Bool\n # Returns `true` if this enum value equals `Float`\n def float? : ::Bool\n # Returns `true` if this enum value equals `Int`\n def int? : ::Bool\n # Returns `true` if this enum value equals `Null`\n def null? : ::Bool\n # Returns `true` if this enum value equals `String`\n def string? : ::Bool\n end\n def bool_value : Bool\n def column_number : Int32\n def float_value : Float64\n def initialize(input : String)\n def int_value : Int64\n def kind : Kind\n def line_number : Int32\n def max_nesting : Int32\n def max_nesting=(value : Int32) : Int32\n # Reads an object, yields once at the value of `key`, and skips the rest.\n def on_key(key : String, & : (-> Nil)) : Nil\n # The number the reader is on, as it was written.\n def raw_value : String\n # The value the reader is on, as an `Any`.\n def read_any : Any\n def read_array(& : (-> Nil)) : Nil\n def read_begin_array : Nil\n def read_begin_object : Nil\n def read_bool : Bool\n def read_end_array : Nil\n def read_end_object : Nil\n # A float, or an integer read as one.\n def read_float : Float64\n def read_int : Int64\n def read_next : Kind\n def read_null : Nil\n def read_null? : Bool\n # Yields each key of the object the reader is on, with the reader at its\n # value. A key named twice is refused, as `JSON.parse` refuses it.\n def read_object(& : (String -> Nil)) : Nil\n def read_object_key : String\n # The value the reader is on, written into `json` as it is read. A number\n # is written as its own text, the text it was read from: rebuilt from the\n # Int64 or Float64 it parsed to, `12345678901234567890` came back as\n # `1.2345678901234567e+19` and `1E+2` as `100.0`, and a UInt64 id passed\n # through here did not read back as itself.\n def read_raw(json : Builder) : Nil\n # The value the reader is on, as JSON text.\n def read_raw : String\n def read_string : String\n # Passes over the value the reader is on, whole.\n def skip : Nil\n def string_value : String\nend\n\n# A type that can write itself into a `Builder`. `JSON.to_json` accepts any\n# value whose type implements it, as a `derive serializable` type does.\npub trait ToJSON\n abstract def to_json(json : Builder) : Nil\nend\n\nimpl ToJSON for Any\n def to_json(json : Builder) : Nil\nend\n\nclass ::Object\n def __json_write(json : ::Std::Json::Builder) : Nil\nend\n" }, { @@ -334,13 +334,13 @@ { "name": "std/levenshtein", "path": "src/std/levenshtein.iyi", - "sourceSha256": "dbf6aaec3fcb2b675100ca0bc5a50244a825b7a35b194a6a64cc58d671f28f4a", + "sourceSha256": "91c4bb5c56be998c453deba9edba400b581ea91526ab792b97cc8cd176de7264", "surface": "module std/levenshtein\n\nmodule ::Levenshtein\n # Computes the Levenshtein distance between two strings.\n def self.distance(string1 : String, string2 : String) : Int32\n def self.find(name : String, tolerance : Int32 | ::Nil = nil, &block : (Finder -> Nil)) : String | ::Nil\n def self.find(name : String, all_names, tolerance : Int32 | ::Nil = nil) : String | ::Nil\n # Optimal string alignment: Levenshtein plus an adjacent transposition as\n # one edit. `cuont` for `count` is two edits by `distance` and one here.\n def self.osa_distance(string1 : String, string2 : String) : Int32\nend\n\nstruct ::Levenshtein::Entry\n def distance : Int32\n def initialize(value : String, distance : Int32)\n def value : String\nend\n\nclass ::Levenshtein::Finder\n def best_match : String | ::Nil\n def self.find(name : String, tolerance : Int32 | ::Nil = nil, &block : (Finder -> Nil)) : String | ::Nil\n def self.find(name : String, all_names, tolerance : Int32 | ::Nil = nil) : String | ::Nil\n def initialize(target : String, tolerance : Int32 | ::Nil = nil)\n def target : String\n def test(name : String, value : String = name) : Nil\n def tolerance : Int32\nend\n" }, { "name": "std/list", "path": "src/std/list.iyi", - "sourceSha256": "c346ae704a3c0134897f35b15e7616c8ffabff4106c40ce5a34dd31e7afd1b1e", + "sourceSha256": "1d082414f483ef7522566d39c2995fd25288211235d108743d4cdc789155a65f", "surface": "module std/list\n\n# The store is built at construction and never handed out, so no method of this\n# type can be observed to change it. That is what makes the value shareable\n# under III.4.4 — every field immutable after construction — and it is why the\n# constructor **copies**.\n#\n# The copy is not defensive habit, it is the missing borrow checker. `List`\n# cannot express \"I own this array now\", so a caller that kept a reference to\n# what it passed in could mutate the list from underneath a task holding it.\n# Rust says this with ownership and pays nothing; here it costs one copy at the\n# boundary, once, and it is the honest price of not having that machinery.\npub struct List(T)\n # Two lists are the same value when they hold the same elements in the same\n # order, which is the whole of what a list is. Until this method, the one\n # answering was `Object#==` (src/iyi/object.iyi:37-39), the constant\n # `false` — not even reflexive, so `list == list` was false too, and every\n # collection that asks said no: `includes?`, `index` and `uniq` all go\n # through `==`, a list could never be found in a `Hash`, and a `case` over\n # a list matched no `when`, because a `when` is `===` and `===` is `==`.\n # That is the failure object.iyi:111-120 wrote up for `Tuple`, reproduced\n # by the one type III.4.7 added for handing a value between tasks — where\n # comparing what two tasks produced is the point.\n #\n # Sizes first, because that is the cheap half of the answer. No shared-store\n # fast path like `Slice#same?`: the constructor copies, so two lists never\n # share a store, and the comparison would pay for every call to fire only\n # when a list is compared with itself.\n #\n # Restricted to `List(T)`, so a list of another element type is unequal by\n # the prelude's default rather than a compile error — the reasoning\n # object.iyi:119-120 gives for restricting `Tuple#==` to its own shape.\n def ==(other : List(T)) : Bool\n # Indexed with `[]`, because every other sequence here is — `Slice`\n # (std/slice.iyi:107), the prelude's `Array` (src/iyi/array.iyi:78) and\n # everything under `Indexable` (std/indexable.iyi:75) — and III.1.7a says a\n # name shared with Crystal means the same thing here. A negative index\n # counts from the end, so `list[-1]` is the last element; that rule was\n # already true through the backing array and unwritten, which is worse than\n # either answer.\n #\n # The out-of-range sentence is raised here rather than left to the store.\n # The array's wording is the right wording — it names the index asked for\n # and how many elements there are, and names no receiver, so there is\n # nothing in it about an array the caller never handed over — but a message\n # this type promises is this file's, not one inherited by accident from a\n # field nobody can see.\n def [](index : Int32) : T\n # The nilable partner, which is the one question `[]` cannot answer: \"the\n # element there, or nothing\". Without it every caller probing a list past\n # its end rewrites the bounds rule above by hand against `size`, and two\n # copies of a rule is one too many.\n def []?(index : Int32) : T | ::Nil\n protected def adopt(items : Array(T)) : Nil\n # Every operation that would change a sequence returns a new one instead, so\n # by III.1.7's convention they are all participles. The plain verbs — `push`,\n # `sort`, `reverse` — name the mutating form, and this type has none. That the\n # naming rule falls out rather than being applied is the point of having\n # settled it before any library code existed.\n #\n # Each builds its array at the size it ends with, once. `dup` then `<<`\n # copied the list, grew the copy to twice its size and handed it to the\n # constructor, which copied it a third time: an append to a list of n\n # elements wrote 4n words where n + 1 is the whole of the work.\n def appended(item : T) : List(T)\n # `at` is the name this file had before `[]`, and samples/iyi/immutable.iyi\n # is written in it. It stays, spelled out, and delegates — so both names\n # exist but only one bounds rule does.\n def at(index : Int32) : T\n def concatenated(other : List(T)) : List(T)\n def empty? : Bool\n # Hashing follows equality, or the agreement above buys nothing: a `Hash`\n # finds a key by its slot first and compares second, so two equal lists in\n # different slots are still two keys. `Object#hash` answered with the type\n # id — every `List(Int32)` in one slot, \"correct and useless\" in exactly\n # the way object.iyi:20-23 says of a key type that never said how it\n # hashes.\n #\n # The array's own hash, which is `Indexable#hash`'s mixing (the prelude's\n # `Array#hash`): one function, so a list and the array it was built from\n # land in the same slot.\n def hash : Int32\n def initialize(items : Array(T))\n # Written down the same way it prints, because a list has no second form:\n # the elements inside it are already inspected. Stated rather than left to\n # `Object#inspect` so it is in this module's exported surface, which is\n # what a list nested in another collection is asked for — `List[List[1]]`\n # rather than a type name — and it is how `Slice` states it too.\n def inspect : String\n # A list over an array nothing else holds: the one `appended` or\n # `concatenated` has just built. The copy above is for a caller's array,\n # which the caller still has; one made here has no other owner.\n protected def self.owning(items : Array(T)) : List(T)\n def size : Int32\n # A list prints its elements, like both of its siblings: `Slice[10, 20, 30]`\n # and the array's `[10, 20, 30]`. Inherited, it printed\n # `Std::List::List(Int32)`, which object.iyi:7-8 calls what \"a value that\n # has said nothing about itself can honestly print\" — and a sequence has\n # plenty to say, so the default was the type failing to answer.\n #\n # The elements are *inspected* rather than printed, which is `Slice#to_s`'s\n # reasoning (std/slice.iyi:353-364): a list of strings reads back as\n # `List[\"pear\", \"fig\"]`, telling a reader the elements are strings, where\n # printing them loses that with no way to get it back.\n def to_s : String\nend\n\n# And the lazy half, for the same reason `std/slice` has one: a `List` could\n# only reach the iterator tower through `to_a`, which copies the sequence\n# the type exists to hand around without copying.\n#\n# A class, as `ArrayIterator` is (std/iterator.iyi): an adaptor keeps its\n# source by value, and this struct was copied into it, so\n# `it.first(2).to_a` and then `it.to_a` gave [1, 2] and [1, 2, 3, 4] where\n# an iterator over an array gives [1, 2] and [3, 4].\npub class ListIterator(T)\n def initialize(list : List(T))\n def next : T | Stop\nend\n\nimpl Std::Enumerable::Enumerable for List(T) forall T\n def each(& : (T -> Nil)) : Nil\nend\n\nimpl Std::Iterator::Iterator for ListIterator(T) forall T\nend\n" }, { @@ -352,32 +352,32 @@ { "name": "std/mail", "path": "src/std/mail.iyi", - "sourceSha256": "205af4ec59cbf503bcb8e22a51fdcaa90bdc89d86e281a67ee6b170fdbdc5d85", + "sourceSha256": "4315722a56c9016e972877b4e17054aa32552363282ab7564fc4ac21a8968025", "surface": "module std/mail\n\npub struct Mail\n # A mailbox: an address and the name shown with it.\n class Address\n def ==(other : Address) : Bool\n # The addr-spec: the local part quoted where it is not a dot-atom.\n def address : String\n def domain : String\n # What `parse` makes, already read.\n def initialize(local : String, domain : String, name : String, read : Bool)\n # *address* is an addr-spec: `user@example.com`, or\n # `\"john doe\"@example.com` with its local part quoted. Anything else\n # is a panic, as is a name with a line break in it.\n def initialize(address : String, name : String = \"\")\n # The local part, unquoted, and the domain as written.\n def local : String\n # The display name, decoded; \"\" when there is none.\n def name : String\n # The one mailbox *text* holds.\n def self.parse(text : String) : Address | MailError\n # The mailboxes of an address list, a group's members in its place.\n def self.parse_list(text : String) : Array(Address) | MailError\n # As a person reads it: `Name `, the name quoted\n # when it is not atoms, nothing encoded.\n def to_s : String\n # Whether the address is more than ASCII (RFC 6532), which only a\n # server that offers SMTPUTF8 takes.\n def utf8? : Bool\n end\n # A message to be written: its addresses, subject, date and identifier,\n # its text and HTML, and its attachments.\n class Message\n protected def self.addresses(name : String, list : Array(Address)) : String\n # A file sent with the message, under *filename*, as *content_type*:\n # the type its extension names (std/mime) when nil.\n def attach(filename : String, data : String, content_type : String | ::Nil = nil) : self\n protected def self.attachment(filename : String, kind : String, data : String) : MailEntity\n def bcc : Array(Address)\n def cc : Array(Address)\n def date : String | ::Nil\n # An RFC 5322 date-time - `Mail.date` writes one - refused when it is\n # not one.\n def date=(value : String) : String\n # `filename=\"...\"` for a short name of printable ASCII; RFC 2231's\n # `filename*=utf-8''...` for any other, in continuations of whole\n # characters when it is longer - 50 encoded bytes in the first, after\n # its charset, and 60 in the rest, so each fits a line. All but the\n # last end in `;`.\n protected def self.filename_params(filename : String) : Array(String)\n def from : Address | ::Nil\n def from=(value : Address | ::Nil) : Address | ::Nil\n # A header of the caller's own, written as unstructured text. The\n # headers the message writes itself are refused here.\n def header(name : String, value : String) : self\n def html : String | ::Nil\n def html=(value : String) : String\n def initialize\n def message_id : String | ::Nil\n # ``, the brackets added when they are not there.\n def message_id=(value : String) : String\n # The parts under one boundary, made from their own hash.\n protected def self.multipart(subtype : String, children : Array(MailEntity)) : MailEntity\n # Every address the message goes to - To, Cc and Bcc - once each.\n def recipients : Array(String)\n def reply_to : Array(Address)\n # ASCII without NUL, in lines of 78 or fewer.\n protected def self.seven_bit?(text : String) : Bool\n def subject : String | ::Nil\n def subject=(value : String) : String\n def text : String | ::Nil\n def text=(value : String) : String\n protected def self.text_part(subtype : String, text : String) : MailEntity\n def to : Array(Address)\n # The message, CRLF line breaks and all. A From and a Date are\n # required - set the Date with `msg.date = Mail.date(...)` - and the\n # Message-ID is optional. Writing a message the caller left incomplete\n # is a mistake, so it panics, with one of:\n #\n # - `mail: a message has no From` when no From was set;\n # - `mail: a message has no Date` when no Date was set.\n #\n # Those are the only panics here: every other value - a header, an\n # address, a Date, a Message-ID, a body, an attachment - is checked,\n # and refused with a panic, by the setter or method that takes it.\n def to_s : String\n # Whether an address in the message is RFC 6532's UTF-8.\n def utf8? : Bool\n end\n # A message or one of its parts, as read: its header fields unfolded and\n # in order, and its body - a multipart's parts, a `message/rfc822`'s\n # message, or bytes in a transfer encoding.\n class Part\n # The mailboxes of every field named *name* - `From`, `To`, `Cc` -\n # none when there is no such field.\n def addresses(name : String) : Array(Address) | MailError\n def attachment? : Bool\n # The parts whose disposition is `attachment`, in order.\n def attachments : Array(Part)\n # The bytes the body stands for, its transfer encoding undone.\n def body : String | MailError\n # The charset text is in: `us-ascii` when none is named (RFC 2045\n # section 5.2), in lower case.\n def charset : String\n # The disposition in lower case - `attachment`, `inline` - or nil.\n def disposition : String | ::Nil\n # The file name of Content-Disposition, or else Content-Type's `name`.\n def filename : String | ::Nil\n # The first part of *media_type* that is not an attachment, depth\n # first: `find(\"text/plain\")` is the text a reader shows.\n def find(media_type : String) : Part | ::Nil\n protected def self.first(headers : Array(::Tuple(String, String)), lower : String) : String | ::Nil\n # The first field named *name*, any case, its encoded-words decoded.\n def header(name : String) : String | ::Nil\n # Every field named *name*, in order, decoded.\n def header_values(name : String) : Array(String)\n # Each field's name as written and its value unfolded, whitespace at\n # either end dropped, nothing decoded.\n def headers : Array(::Tuple(String, String))\n def initialize(source : String, headers : Array(::Tuple(String, String)), media_type : String, params : Hash(String, String), body_at : Int32, body_end : Int32, parts : Array(Part))\n # `type/subtype` in lower case: `text/plain` when the part names none,\n # or names one that cannot be read (RFC 2045 section 5.2).\n def media_type : String\n def multipart? : Bool\n # A Content-Type parameter, RFC 2231's decoded.\n def param(name : String) : String | ::Nil\n def self.parse(text : String) : Part | MailError\n # The parts of a multipart, or the message a `message/rfc822` holds.\n def parts : Array(Part)\n protected def self.prefix?(a : Pointer(UInt8), b : Pointer(UInt8), size : Int32) : Bool\n # The first field named *name*, as written.\n def raw_header(name : String) : String | ::Nil\n protected def self.read(source : String, start : Int32, stop : Int32, depth : Int32, default_type : String) : Part | MailError\n # The parts of a multipart body: between `--boundary` lines, the line\n # break before each line its own; to the `--boundary--` line. The\n # preamble and the epilogue are passed over.\n protected def self.split(source : String, start : Int32, stop : Int32, boundary : String, media : String) : Array(::Tuple(Int32, Int32)) | MailError\n def subject : String | ::Nil\n # The body as text in UTF-8, from the part's charset, each CRLF an LF.\n def text : String | MailError\n # The transfer encoding in lower case: `7bit` when none is named.\n def transfer_encoding : String\n protected def self.trim(value : String) : String\n # This part, then each under it, depth first.\n def walk(&block : (Part -> Nil)) : Nil\n end\n # RFC 5322's date-time for *unix* seconds, at *offset* minutes east of\n # UTC: `Sat, 17 Oct 2026 14:00:00 +0200`, as `email.utils.format_datetime`\n # writes it.\n def self.date(unix : Int64, offset : Int32 = 0) : String\n # What base64 *text* stands for, characters outside the alphabet passed\n # over.\n def self.decode_base64(text : String) : String\n # *text* from *charset* in UTF-8.\n def self.decode_charset(text : String, charset : String) : String | MailError\n # What quoted-printable *text* stands for, hard breaks as CRLF.\n def self.decode_quoted_printable(text : String) : String\n # *text* with its RFC 2047 encoded-words decoded.\n def self.decode_words(text : String) : String\n # *data* in base64, lines of 76 and a CRLF after each.\n def self.encode_base64(data : String) : String\n # *data* in quoted-printable, lines of 76 or fewer; its line breaks\n # hard ones written as CRLF, or with *binary* escaped like any byte.\n def self.encode_quoted_printable(data : String, binary : Bool = false) : String\n # An unstructured header line, `name: value` folded and encoded as the\n # builder writes it, CRLF and all.\n def self.fold(name : String, value : String) : String\n def initialize\n # A message read from *text*: its header section, then its body.\n def self.parse(text : String) : Part | MailError\n # A date-time (RFC 5322 section 3.3, with section 4.3's obsolete years\n # and zones) as Unix seconds and the offset it was written at, in\n # minutes. The day of the week is not held to the date, as readers do\n # not hold it. A military zone is -0000, as section 4.3 says.\n def self.parse_date(text : String) : ::Tuple(Int64, Int32) | MailError\nend\n\n# What a message, a header or an address is that it should not be, as a\n# value: `reason` says what; `message` says it is mail's.\npub struct MailError\n def initialize(reason : String)\n def reason : String\n def to_s : String\nend\n\nimpl Error for MailError\n def message : String\nend\n" }, { "name": "std/math", "path": "src/std/math.iyi", - "sourceSha256": "c4a4757ccdfb7d46e982de1177ad20663690a0040f630cd33a3a822117819b36", + "sourceSha256": "d41a2f40aa4af8d64c60cdff5971d9b346296ca2026e171ed1143e46f2e0fb0e", "surface": "module std/math\n\npub struct Math\n def self.acos(value : Float64) : Float64\n def self.acos(value : Float32) : Float32\n def self.acos(value : T) : Float64 forall T\n # Correctly rounded (CORE-MATH, e_acosh.c): NaN below 1 (and for NaN),\n # acosh(1) = 0, acosh(inf) = inf.\n def self.acosh(value : Float64) : Float64\n def self.acosh(value : Float32) : Float32\n def self.acosh(value : T) : Float64 forall T\n def self.asin(value : Float64) : Float64\n def self.asin(value : Float32) : Float32\n def self.asin(value : T) : Float64 forall T\n # Correctly rounded (CORE-MATH, s_asinh.c): odd, asinh(+-0) = +-0,\n # +-inf and NaN come back as x + x.\n def self.asinh(value : Float64) : Float64\n def self.asinh(value : Float32) : Float32\n def self.asinh(value : T) : Float64 forall T\n def self.atan(value : Float64) : Float64\n def self.atan(value : Float32) : Float32\n def self.atan(value : T) : Float64 forall T\n # The angle of (x, y), with the zeros' and infinities' signs read the\n # way C's atan2 reads them: atan2(0.0, -0.0) is pi, atan2(-0.0, -1.0) is\n # -pi, atan2(inf, inf) is pi/4.\n def self.atan2(y : Float64, x : Float64) : Float64\n def self.atan2(y : Float32, x : Float32) : Float32\n def self.atan2(y : T, x : U) : Float64 forall T, U\n # Correctly rounded (CORE-MATH, e_atanh.c): odd, atanh(+-1) = +-inf, NaN\n # beyond 1 in magnitude (and for NaN).\n def self.atanh(value : Float64) : Float64\n def self.atanh(value : Float32) : Float32\n def self.atanh(value : T) : Float64 forall T\n # The Bessel function of the first kind of the given *order*,\n # Jn(*value*), as glibc computes it.\n def self.besselj(order : Int32, value : Float64) : Float64\n # The Bessel function of the first kind of the given *order*,\n # Jn(*value*), as glibc computes it.\n def self.besselj(order : Int32, value : Float32) : Float32\n # The Bessel function of the first kind of the given *order*,\n # Jn(*value*), as glibc computes it.\n def self.besselj(order : Int32, value : T) : Float64 forall T\n # The Bessel function of the first kind of order 0, J0(*value*), as glibc\n # computes it.\n def self.besselj0(value : Float64) : Float64\n # The Bessel function of the first kind of order 0, J0(*value*), as glibc\n # computes it.\n def self.besselj0(value : Float32) : Float32\n # The Bessel function of the first kind of order 0, J0(*value*), as glibc\n # computes it.\n def self.besselj0(value : T) : Float64 forall T\n # The Bessel function of the first kind of order 1, J1(*value*), as glibc\n # computes it.\n def self.besselj1(value : Float64) : Float64\n # The Bessel function of the first kind of order 1, J1(*value*), as glibc\n # computes it.\n def self.besselj1(value : Float32) : Float32\n # The Bessel function of the first kind of order 1, J1(*value*), as glibc\n # computes it.\n def self.besselj1(value : T) : Float64 forall T\n # The Bessel function of the second kind of the given *order*,\n # Yn(*value*), as glibc computes it: NaN for negative *value*, -infinity\n # at zero (+infinity for odd negative *order*).\n def self.bessely(order : Int32, value : Float64) : Float64\n # The Bessel function of the second kind of the given *order*,\n # Yn(*value*), as glibc computes it: NaN for negative *value*, -infinity\n # at zero (+infinity for odd negative *order*).\n def self.bessely(order : Int32, value : Float32) : Float32\n # The Bessel function of the second kind of the given *order*,\n # Yn(*value*), as glibc computes it: NaN for negative *value*, -infinity\n # at zero (+infinity for odd negative *order*).\n def self.bessely(order : Int32, value : T) : Float64 forall T\n # The Bessel function of the second kind of order 0, Y0(*value*), as glibc\n # computes it: NaN for negative *value*, -infinity at zero.\n def self.bessely0(value : Float64) : Float64\n # The Bessel function of the second kind of order 0, Y0(*value*), as glibc\n # computes it: NaN for negative *value*, -infinity at zero.\n def self.bessely0(value : Float32) : Float32\n # The Bessel function of the second kind of order 0, Y0(*value*), as glibc\n # computes it: NaN for negative *value*, -infinity at zero.\n def self.bessely0(value : T) : Float64 forall T\n # The Bessel function of the second kind of order 1, Y1(*value*), as glibc\n # computes it: NaN for negative *value*, -infinity at zero.\n def self.bessely1(value : Float64) : Float64\n # The Bessel function of the second kind of order 1, Y1(*value*), as glibc\n # computes it: NaN for negative *value*, -infinity at zero.\n def self.bessely1(value : Float32) : Float32\n # The Bessel function of the second kind of order 1, Y1(*value*), as glibc\n # computes it: NaN for negative *value*, -infinity at zero.\n def self.bessely1(value : T) : Float64 forall T\n def self.cbrt(value : Float64) : Float64\n def self.cbrt(value : Float32) : Float32\n def self.cbrt(value : T) : Float64 forall T\n # The magnitude of mag with the sign of sgn. Any other pair of numbers is\n # taken as two Float64s, as the other library's untyped overload takes\n # it: `copysign(3, -1)` was an ambiguous call between the two float\n # overloads, as `max(2, 5)` was.\n def self.copysign(mag : Float64, sgn : Float64) : Float64\n def self.copysign(mag : Float32, sgn : Float32) : Float32\n def self.copysign(mag : T, sgn : U) : Float64 forall T, U\n def self.cos(value : Float64) : Float64\n def self.cos(value : Float32) : Float32\n def self.cos(value : T) : Float64 forall T\n def self.cosh(value : Float64) : Float64\n def self.cosh(value : Float32) : Float32\n def self.cosh(value : T) : Float64 forall T\n # The error function, correctly rounded: glibc 2.43's CORE-MATH erf\n # (s_erf.c). NaN answers NaN, ±inf answer ±1, and ±0 keep their sign.\n def self.erf(value : Float64) : Float64\n # The single's: the double's answer rounded, which is the correctly\n # rounded single but where rounding twice meets a halfway point.\n def self.erf(value : Float32) : Float32\n def self.erf(value : T) : Float64 forall T\n # 1 - erf(value), correctly rounded: glibc 2.43's CORE-MATH erfc\n # (s_erfc.c). NaN answers NaN, -inf 2 and +inf 0; the answer underflows\n # to 0 from 0x1.b39dc41e48bfdp+4 (about 27.23) up.\n def self.erfc(value : Float64) : Float64\n # The single's: the double's answer rounded, which is the correctly\n # rounded single but where rounding twice meets a halfway point.\n def self.erfc(value : Float32) : Float32\n def self.erfc(value : T) : Float64 forall T\n def self.exp(value : Float64) : Float64\n def self.exp(value : Float32) : Float32\n def self.exp(value : T) : Float64 forall T\n def self.exp2(value : Float64) : Float64\n def self.exp2(value : Float32) : Float32\n def self.exp2(value : T) : Float64 forall T\n def self.expm1(value : Float64) : Float64\n def self.expm1(value : Float32) : Float32\n def self.expm1(value : T) : Float64 forall T\n # a * b + c rounded once, as IEEE 754 defines it and as the other\n # library's `Math.fma` answers through libm. The instruction where the\n # processor has one: every aarch64 processor (`fmadd`, LLVM's intrinsic),\n # and an x86_64 one with FMA3 and an operating system that saves its\n # registers, asked once by `cpuid` and `xgetbv` as `Float64#floor` asks\n # for `roundsd`. Elsewhere musl's `fma` (MIT; Copyright 2005-2020 Rich\n # Felker, et al.), in iyi: the significands as integers, their product's\n # 128 bits exact, the sum aligned with a sticky bit, one rounding at the\n # conversion back. This was Dekker's product and two more additions,\n # which round each, and answered the neighbour of the fused result for\n # arguments that cancel.\n def self.fma(a : Float64, b : Float64, c : Float64) : Float64\n # The single's: the instruction where there is one, and otherwise\n # musl's `fmaf` - the product of two singles is exact in a double and\n # the sum rounds once there, which is right unless that double sits on\n # a single's halfway point or among the subnormals; then the sum's\n # rounding error, which is exact, sends it to the odd neighbour, which\n # the conversion to a single cannot then misround.\n def self.fma(a : Float32, b : Float32, c : Float32) : Float32\n def self.fma(a : T, b : U, c : V) : Float64 forall T, U, V\n # value as m * 2^e with m in [0.5, 1) (or 0, NaN or an infinity as it\n # came, with 0), read from the bits: a subnormal is first scaled up by\n # 2^54 so that it has an exponent field of its own.\n def self.frexp(value : Float64) : ::Tuple(Float64, Int32)\n def self.gamma(value : Float64) : Float64\n # The single's: the double's answer rounded, which is the correctly\n # rounded single but where rounding twice meets a halfway point.\n def self.gamma(value : Float32) : Float32\n def self.gamma(value : T) : Float64 forall T\n # Euclid on the side of zero where every magnitude fits: the non-positive\n # side for a signed type, its minimum included - gcd(-2147483648, 6) is 2\n # - and the non-negative side, the only one there is, for an unsigned\n # type. Only gcd(minimum, 0), whose answer does not fit, overflows. The\n # two arguments are one type, as `%` takes one: the signature took any\n # two integers, and an unsigned pair (it has no unary minus) or a pair of\n # widths (no `%` between them) failed to compile inside this method. A\n # pair of widths is refused at the call now, and a literal takes the\n # other argument's type.\n def self.gcd(a : T, b : T) : T forall T\n def self.hypot(x : Float64, y : Float64) : Float64\n def self.hypot(x : Float32, y : Float32) : Float32\n def self.hypot(x : T, y : U) : Float64 forall T, U\n # The exponent of value as an integer, as C's ilogb and glibc answer it:\n # a subnormal's true exponent, -2147483648 for zero and NaN, 2147483647\n # for an infinity.\n def self.ilogb(value : Float64) : Int32\n def self.ilogb(value : Float32) : Int32\n def self.ilogb(value : T) : Int32 forall T\n def initialize\n # The largest r with r * r <= value, in the value's own type, by the\n # other library's bit-at-a-time method. A negative value panics, where\n # that library raises.\n def self.isqrt(value : ::Int) : ::Int\n def self.lcm(a : T, b : T) : T forall T\n # value * 2^exponent, with one rounding: the exponent field is rewritten\n # outright, and a result that lands among the subnormals is scaled down\n # from a normal one in a single step.\n def self.ldexp(value : Float64, exponent : Int32) : Float64\n # log|gamma(value)|, correctly rounded (CORE-MATH, as glibc 2.43): the\n # poles at the non-positive whole numbers and both infinities answer\n # +infinity, NaN answers NaN.\n def self.lgamma(value : Float64) : Float64\n # The single's: the double's answer rounded, which is the correctly\n # rounded single but where rounding twice meets a halfway point.\n def self.lgamma(value : Float32) : Float32\n def self.lgamma(value : T) : Float64 forall T\n def self.log(value : Float64, base : Float64) : Float64\n def self.log(value : T, base : U) : Float64 forall T, U\n def self.log(value : Float64) : Float64\n def self.log(value : Float32) : Float32\n def self.log(value : T) : Float64 forall T\n def self.log10(value : Float64) : Float64\n def self.log10(value : Float32) : Float32\n def self.log10(value : T) : Float64 forall T\n def self.log1p(value : Float64) : Float64\n def self.log1p(value : Float32) : Float32\n def self.log1p(value : T) : Float64 forall T\n def self.log2(value : Float64) : Float64\n def self.log2(value : Float32) : Float32\n def self.log2(value : T) : Float64 forall T\n # The exponent of value as a double: -inf for zero, +inf for an\n # infinity, NaN for NaN, as C's logb.\n def self.logb(value : Float64) : Float64\n def self.logb(value : Float32) : Float32\n def self.logb(value : T) : Float64 forall T\n def self.max(a : T, b : T) : T forall T\n # min and max take two values of one type, any type `<` compares, and\n # answer one of them. Each is one overload: they were a Float64 and a\n # Float32 one, which took no integer pair, and an integer overload beside\n # them ties with them wherever an integer variable can widen to a\n # double: with all three, `Math.max(1_i64, x)` with `x : Int32` is\n # \"ambiguous call, implicit cast of Int32 matches all of Float64,\n # Int64\". Two doubles or two singles are ordered as IEEE 754-2019's\n # minimumNumber and maximumNumber order them, as they were: the number\n # when one side is NaN, and -0.0 below 0.0. Every other type compares\n # with `<` alone and answers the first argument on a tie, as the other\n # library's untyped `Math.min` and `Math.max` do. The one type is the\n # first argument's that the other can be cast to, so `Math.max(2,\n # page - 1)` is an Int32, `Math.max(0, n)` with `n : Int64` an Int64,\n # `Math.max(2, 2.5)` the Float64 2.5, and `Math.max(n, 0.5)` with\n # `n : Int32` a Float64, as it was; a pair neither of which casts to the\n # other's type, an Int64 and a UInt64 or a number and a String, is\n # refused at the call.\n def self.min(a : T, b : T) : T forall T\n def self.pow(base : Float64, exp : Float64) : Float64\n def self.pow(base : Float32, exp : Float32) : Float32\n def self.pow(base : T, exp : U) : Float64 forall T, U\n # The least power of two not below v, 1 for v <= 1, in v's type; one\n # the type cannot hold panics \"arithmetic overflow\".\n def self.pw2ceil(v : ::Int) : ::Int\n # scalbn with an Int64 exponent: past the range an Int32 holds every\n # answer is zero or an infinity already, so the exponent is clamped.\n def self.scalbln(value : Float64, exp : Int64) : Float64\n def self.scalbln(value : Float32, exp : Int64) : Float32\n def self.scalbln(value : T, exp : U) : Float64 forall T, U\n # value * 2^exp, rounded once: `ldexp` under C's other name.\n def self.scalbn(value : Float64, exp : Int32) : Float64\n def self.scalbn(value : Float32, exp : Int32) : Float32\n def self.scalbn(value : T, exp : U) : Float64 forall T, U\n def self.sin(value : Float64) : Float64\n def self.sin(value : Float32) : Float32\n def self.sin(value : T) : Float64 forall T\n # sinh, cosh and tanh as glibc 2.43 computes them - fdlibm's\n # `e_sinh.c`, `e_cosh.c` and `s_tanh.c` (Sun's notice is at `expm1`), on\n # the `exp` and `expm1` above, which are glibc's to the last bit, so\n # these are too. Their branches are fdlibm's, at its thresholds, read off\n # the high word: a series near zero and `half_exp` past 709 answered\n # differently in the last bit for 952 arguments of sinh in 100,000, 217\n # of cosh and 4,863 of tanh.\n def self.sinh(value : Float64) : Float64\n def self.sinh(value : Float32) : Float32\n def self.sinh(value : T) : Float64 forall T\n def self.sqrt(value : Float64) : Float64\n def self.sqrt(value : Float32) : Float32\n def self.sqrt(value : T) : Float64 forall T\n def self.tan(value : Float64) : Float64\n def self.tan(value : Float32) : Float32\n def self.tan(value : T) : Float64 forall T\n def self.tanh(value : Float64) : Float64\n def self.tanh(value : Float32) : Float32\n def self.tanh(value : T) : Float64 forall T\n # gamma(value), correctly rounded (CORE-MATH, as glibc 2.43): +-0 answer\n # +-infinity, the negative whole numbers and -infinity NaN; past\n # 171.62 the answer overflows to infinity, and below -184 it is a zero\n # carrying gamma's sign.\n def self.tgamma(value : Float64) : Float64\n # The single's: the double's answer rounded, which is the correctly\n # rounded single but where rounding twice meets a halfway point.\n def self.tgamma(value : Float32) : Float32\n def self.tgamma(value : T) : Float64 forall T\nend\n" }, { "name": "std/mime", "path": "src/std/mime.iyi", - "sourceSha256": "a1cac6eb0a4b5a6da16cd36b295720e3d9b01abce0344518a311d168070f99c2", + "sourceSha256": "7d9220d8e14385345930b49861cb371c9882bc1d5a178d80934fa178abb147bd", "surface": "module std/mime\n\npub struct MIME\n # Whether a body of this type shrinks under gzip or deflate: text,\n # JSON, JavaScript, XML and their `+json`/`+xml` kin (SVG among them),\n # YAML, TOML, scripts, WebAssembly, uncompressed fonts and archives,\n # bitmaps and icons. Not images, audio and video that are already\n # compressed, nor zip, gzip and the other archives, nor WOFF and WOFF2,\n # which are compressed fonts. WebAssembly is included because its code\n # roughly halves under gzip, which is why servers compress it.\n def self.compressible?(media_type : String) : Bool\n # The extensions a media type is written with, dotted and sorted\n # (\"text/html\" is [\".htm\", \".html\"]). Parameters are ignored and case\n # does not matter; empty when the type is unknown. The array is a fresh\n # copy the caller may change.\n def self.extensions(media_type : String) : Array(String)\n # The same, falling back to *default*.\n def self.from_extension(name : String, default : String = \"application/octet-stream\") : String\n # The media type for a file name or extension: \".html\", \"html\",\n # \"index.HTML\" and \"a/b.tar.gz\" (by its last extension) all work, in\n # any case. A path whose base name has no dot has no extension; a bare\n # word is taken as the extension itself. Nil when unknown.\n def self.from_extension?(name : String) : String | ::Nil\n def initialize\n # RFC 9110 §8.3.1: `type/subtype` followed by `; name=value` parameters,\n # whitespace allowed around the semicolons and at either end, a value a\n # token or a quoted string whose backslash escapes are undone. Answers\n # the type lowercased and the parameters with lowercased names and\n # values as written; a name given twice keeps its last value. Nil when\n # any part is not a token, a parameter has no `=`, or a quote is left\n # open.\n def self.parse?(value : String) : ::Tuple(String, Hash(String, String)) | ::Nil\n # The media type with `; charset=utf-8` added when it is text, JSON,\n # JavaScript or XML (`+json` and `+xml` included) and names no charset\n # yet. Anything else, and anything that does not parse, comes back as\n # given.\n def self.with_charset(media_type : String) : String\nend\n" }, { "name": "std/mlkem", "path": "src/std/mlkem.iyi", - "sourceSha256": "8b6ae912b1eb37146021806f7dcfff82fc16d6f1d57f38ba723082bfb73cc106", + "sourceSha256": "b56a7fefc67fde1f1bee0124f0873e4f840762948cbdb80d71c2cc8a831b7727", "surface": "module std/mlkem\n\n# ML-KEM-768's encapsulation, and its sizes (FIPS 203 section 8, table 3).\npub struct MlKem768\n # The ciphertext for *encapsulation_key* and the 32-byte shared secret it\n # carries, FIPS 203 Algorithm 17 from the 32 random bytes *randomness*\n # (m), or an `MlKemError`: a key that is not 1,184 bytes or fails the\n # modulus check of section 7.2.\n def self.encapsulate(encapsulation_key : String | Array(UInt8), randomness : String | Array(UInt8)) : ::Tuple(Array(UInt8), Array(UInt8)) | MlKemError\n def initialize\nend\n\n# An ML-KEM-768 key pair: the decapsulation key and the encapsulation key\n# that goes with it. Holds the secret vector in the NTT domain, and the\n# public matrix and vector that decapsulation encrypts with again.\npub struct MlKem768PrivateKey\n # The 32-byte shared secret *ciphertext* carries, FIPS 203 Algorithm 18,\n # or an `MlKemError` for a ciphertext that is not 1,088 bytes. A\n # ciphertext that does not re-encrypt to itself is answered with the\n # implicit-rejection key J(z || c), chosen by masks: the caller cannot\n # tell the two apart, and neither can the time taken.\n def decapsulate(ciphertext : String | Array(UInt8)) : Array(UInt8) | MlKemError\n # The 2,400-byte decapsulation key, as `from_bytes` reads it.\n def decapsulation_key : Array(UInt8)\n # The 1,184 bytes a peer encapsulates to: ByteEncode12 of t, then rho.\n def encapsulation_key : Array(UInt8)\n # The key whose 2,400-byte decapsulation key is *bytes* (ByteEncode12 of\n # s, the encapsulation key, H of it, z), or an `MlKemError`: the wrong\n # size, or a stored H(ek) that is not the encapsulation key's hash (FIPS\n # 203 section 7.3).\n def self.from_bytes(bytes : String | Array(UInt8)) : MlKem768PrivateKey | MlKemError\n # The key pair of *seed*, d then z, 32 bytes each: FIPS 203 Algorithm 16\n # (ML-KEM.KeyGen_internal) over Algorithm 13 (K-PKE.KeyGen).\n def self.from_seed(seed : String | Array(UInt8)) : MlKem768PrivateKey\n def initialize(encapsulation_key : Array(UInt8), s : Pointer(UInt32), t : Pointer(UInt32), a : Pointer(UInt32), h : Array(UInt8), z : Array(UInt8))\nend\n\n# What is wrong with a key or a ciphertext from outside: `message` is the\n# sentence.\npub struct MlKemError\n def initialize(message : String)\n def to_s : String\nend\n\nimpl Error for MlKemError\n def message : String\nend\n" }, { "name": "std/mutex", "path": "src/std/mutex.iyi", - "sourceSha256": "e02a2548bfddd8681125bbb3c18272d4b266a1df4c1e04f3345d7c47109b2f3d", - "surface": "module std/mutex\n\npub class Mutex(T)\n # The program's one thread, with nothing on it to run but the waiting\n # task: no task runnable, asleep or waiting on I/O, and none another\n # thread has woken into its inbox (SPEC.md III.4.13). Nothing can\n # release or change the value then.\n protected def self.alone? : Bool\n protected def self.build(value : T) : Mutex(T)\n # The block's answer, worked out with the lock held. It must be `Share`,\n # because it leaves the lock.\n def get(&block : (T -> R)) : R | Cancelled forall R\n # Whether the holder, which took the lock on *thread*, may be running\n # now: another thread exists, and the holder is not on this one, which\n # is running the waiter.\n protected def self.holder_may_run?(thread : UInt64) : Bool\n # Runs the block with the value, the lock held; answers nil, or\n # `Cancelled` when the task was cancelled while it waited.\n def lock(&block : (T -> _)) : Nil | Cancelled\n # Whether some task holds it now: an answer that may be old by the time\n # it is read, for a report rather than a decision.\n def locked? : Bool\n # The value is the block's: made here, so nothing outside holds it. The\n # constructor that takes a value is private for that reason.\n def self.new(&block : (-> U)) : Mutex(U) forall U\n protected def self.task_id : UInt64\n protected def self.thread_id : UInt64\n # The value replaced by the block's answer, with the lock held: what a\n # number or any other value type wants, which a block cannot change in\n # place.\n def update(&block : (T -> T)) : Nil | Cancelled\n # Runs the block with the lock held each time the value changes, until it\n # answers something other than nil, and answers that: a condition and\n # what is done once it holds, in one step no other task can come between.\n #\n # queue = Mutex.new { [] of Int32 }\n # job = queue.wait_for { |q| q.empty? ? nil : q.shift }.or_panic\n #\n # A change is any lock, get, update or wait_for of this Mutex by another\n # task; between them the waiter holds no lock, and is parked on the count\n # of releases until another task's moves it. Cancellable, as `lock` is.\n # A wait nothing could end - one thread, and nothing else on it to run -\n # panics saying so.\n def wait_for(&block : (T -> R | ::Nil)) : R | Cancelled forall R\nend\n" + "sourceSha256": "d51acd021ce19a9fa53b70b864dd0af72b83593a89eb1adbddd1e17b017016ed", + "surface": "module std/mutex\n\npub class Mutex(T)\n # The program's one thread, with nothing on it to run but the waiting\n # task: no task runnable, asleep or waiting on I/O, and none another\n # thread has woken into its inbox (SPEC.md III.4.13). Nothing can\n # release or change the value then.\n protected def self.alone? : Bool\n protected def self.build(value : T) : Mutex(T)\n # The block's answer, worked out with the lock held. It must be `Share`,\n # because it leaves the lock.\n def get(&block : (T -> R)) : R | Cancelled forall R\n # Whether the holder, which took the lock on *thread*, may be running\n # now: another thread exists, and the holder is not on this one, which\n # is running the waiter.\n protected def self.holder_may_run?(thread : UInt64) : Bool\n # Runs the block with the value, the lock held; answers nil, or\n # `Cancelled` when the task was cancelled while it waited.\n def lock(&block : (T -> _)) : Nil | Cancelled\n # Whether some task holds it now: an answer that may be old by the time\n # it is read, for a report rather than a decision.\n def locked? : Bool\n # The value is the block's: made here, so nothing outside holds it. The\n # constructor that takes a value is private for that reason.\n def self.new(&block : (-> U)) : Mutex(U) forall U\n protected def self.relaxed_get(word : Pointer(Atomic(UInt64))) : UInt64\n protected def self.relaxed_set(word : Pointer(Atomic(UInt64)), value : UInt64) : Nil\n protected def self.task_id : UInt64\n protected def self.thread_id : UInt64\n # The value replaced by the block's answer, with the lock held: what a\n # number or any other value type wants, which a block cannot change in\n # place.\n def update(&block : (T -> T)) : Nil | Cancelled\n # Runs the block with the lock held each time the value changes, until it\n # answers something other than nil, and answers that: a condition and\n # what is done once it holds, in one step no other task can come between.\n #\n # queue = Mutex.new { [] of Int32 }\n # job = queue.wait_for { |q| q.empty? ? nil : q.shift }.or_panic\n #\n # A change is any lock, get, update or wait_for of this Mutex by another\n # task; between them the waiter holds no lock, and is parked on the count\n # of releases until another task's moves it. Cancellable, as `lock` is.\n # A wait nothing could end - one thread, and nothing else on it to run -\n # panics saying so.\n def wait_for(&block : (T -> R | ::Nil)) : R | Cancelled forall R\nend\n" }, { "name": "std/named_tuple", @@ -388,19 +388,19 @@ { "name": "std/netip", "path": "src/std/netip.iyi", - "sourceSha256": "86ea9f02692a33d9c50095a7db3ed2b72cb764f7b4386d176b35c8d531521297", + "sourceSha256": "6ff0290583115e39f7dc104ed5bd0ce451797d100397b77bfdacd7977358bd1f", "surface": "module std/netip\n\n# An IPv4 or IPv6 address, an IPv6 one with its zone (\"\" for none).\npub struct IP\n # IPv4 first, then by value, then by zone (none first).\n def <=>(other : IP) : Int32\n def ==(other : IP) : ::Bool\n # 32 or 128.\n def bit_length : Int32\n # Four or sixteen bytes, network order.\n def bytes : Array(UInt8)\n # Every IPv6 group in four digits (an IPv4-mapped tail kept dotted, as\n # Python writes it); IPv4 as `to_s` writes it.\n def exploded : String\n # Four bytes are an IPv4 address and sixteen an IPv6 one, network order,\n # as `bytes` answers them.\n def self.from_bytes(bytes : Array(UInt8)) : IP\n # Globally reachable by the IANA special-purpose registries, as Python\n # 3.14's `is_global` reads them.\n def global? : ::Bool\n def hash : Int32\n def initialize(*, value : UInt128, v6 : ::Bool, zone : String)\n def inspect : String\n # The IPv4 address inside an IPv4-mapped one (`::ffff:a.b.c.d`), or nil.\n def ipv4_mapped : IP | ::Nil\n # `::ffff:a.b.c.d` for an IPv4 address (RFC 4291 section 2.5.5.2).\n def ipv6_mapped : IP\n # 169.254.0.0/16 (RFC 3927), fe80::/10.\n def link_local? : ::Bool\n # 127.0.0.0/8, ::1.\n def loopback? : ::Bool\n protected def max_value : UInt128\n # 224.0.0.0/4, ff00::/8.\n def multicast? : ::Bool\n # The address after this one, nil after the family's last.\n def next : IP | ::Nil\n # Either family, as Python's `ip_address` reads it: IPv4 when it is\n # IPv4, else IPv6. Text with a colon can only be IPv6 and text without\n # one only IPv4, so the refusal is the one family's that applies.\n def self.parse(text : String) : IP | NetipError\n # Dotted decimal, four octets, Python's `IPv4Address`.\n def self.parse_v4(text : String) : IP | NetipError\n # RFC 4291 text with an optional `%zone`, Python's `IPv6Address`.\n def self.parse_v6(text : String) : IP | NetipError\n # The prefix of *length* bits holding this address, its host bits\n # cleared and its zone left behind.\n def prefix(length : Int32) : Prefix\n # The address before this one, nil before the family's first.\n def prev : IP | ::Nil\n # RFC 1918 (10/8, 172.16/12, 192.168/16) and RFC 4193 (fc00::/7): the\n # networks people number their own machines from, Go's `IsPrivate`.\n def private? : ::Bool\n # 240.0.0.0/4 (RFC 1112's class E), and for IPv6 the blocks RFC 4291\n # section 2.4 leaves unassigned: Python's `is_reserved`.\n def reserved? : ::Bool\n # The name a PTR record for this address is under: `4.3.2.1.in-addr.arpa`,\n # or thirty-two nibbles and `ip6.arpa`.\n def reverse_pointer : String\n # fec0::/10, deprecated by RFC 3879; read off the IPv6 bits alone, as\n # Python reads it, and never IPv4.\n def site_local? : ::Bool\n def to_s(io : IO) : Nil\n def to_s : String\n # The address's bits, network order, in the low 32 for IPv4.\n def to_u128 : UInt128\n # The IPv4 address inside an IPv4-mapped one, any other address itself.\n def unmap : IP\n # 0.0.0.0, ::.\n def unspecified? : ::Bool\n # The IPv4 address *a*.*b*.*c*.*d*.\n def self.v4(a : UInt8, b : UInt8, c : UInt8, d : UInt8) : IP\n # The IPv4 address whose 32 bits, network order, are *value*.\n def self.v4(value : UInt32) : IP\n def v4? : ::Bool\n # The IPv6 address whose 128 bits, network order, are *value*.\n def self.v6(value : UInt128) : IP\n def v6? : ::Bool\n # This address in *zone*, or with none for `\"\"`. Python's rules for a\n # zone hold: no `%` in it. IPv4 has no zones.\n def with_zone(zone : String) : IP\n # The zone, or nil for none (and always for IPv4).\n def zone : String | ::Nil\nend\n\n# Why a text is not an address or a prefix: `message` names the text.\npub struct NetipError\n def initialize(message : String)\n def to_s : String\nend\n\n# An address and a length: a CIDR block (RFC 4632), with the address's\n# host bits as it was given them.\npub struct Prefix\n # IPv4 first, then by first address, then shorter first, then by the\n # address as held - Python's order of networks, host bits after.\n def <=>(other : Prefix) : Int32\n def ==(other : Prefix) : ::Bool\n # 32 or 128.\n def bit_length : Int32\n # Whether *ip* is in the block: never across families, the zone not read.\n def contains?(ip : IP) : ::Bool\n # Whether all of *other* is in this block (Python's `supernet_of`).\n def contains?(other : Prefix) : ::Bool\n # The block's first address (Python's `network_address`).\n def first : IP\n def hash : Int32\n # The host bits as an address: `0.0.0.255` for a /24.\n def hostmask : IP\n # *ip* and *length* as they are, host bits and all; `IP#prefix` clears\n # them. A zoned address is refused: a block has no zone.\n def initialize(ip : IP, length : Int32)\n def inspect : String\n def ip : IP\n # The block's last address (Python's `broadcast_address`).\n def last : IP\n def length : Int32\n # This prefix with the address's host bits cleared.\n def masked : Prefix\n # The length as an address: `255.255.255.0` for a /24.\n def netmask : IP\n # Whether the two blocks share an address: as blocks nest or are\n # apart, one holds the other's first address or neither shares any.\n def overlaps?(other : Prefix) : ::Bool\n # Python's `ip_network(text, strict: !mask)`, a zone refused.\n def self.parse(text : String, mask : ::Bool = false) : Prefix | NetipError\n # The *index*th /*new_length* block of this one, from 0.\n def subnet(new_length : Int32, index : UInt128) : Prefix\n # Whether every address of this block is in *other*.\n def subnet_of?(other : Prefix) : ::Bool\n # The /*new_length* blocks this one splits into, in order.\n def subnets(new_length : Int32) : Array(Prefix)\n # The block of *new_length* holding this one.\n def supernet(new_length : Int32) : Prefix\n # The block one bit shorter; a /0 is its own.\n def supernet : Prefix\n def to_s(io : IO) : Nil\n def to_s : String\n def v4? : ::Bool\n def v6? : ::Bool\nend\n\n# Prefixes of both families in a binary trie, one node a bit: node 0 is\n# the IPv4 root and node 1 the IPv6 one, and a node's children are made\n# after it, so each has a larger index than its parent - which is what\n# lets `collapsed` decide a node from its children in one backward pass.\n# A removed prefix leaves its nodes behind, unmarked.\npub class PrefixSet\n # Adds *prefix*, masked; false when it was there.\n def add(prefix : Prefix) : ::Bool\n # The fewest prefixes holding exactly the addresses the set's do,\n # IPv4 first, in order: Python's `collapse_addresses`, per family.\n def collapsed : Array(Prefix)\n # Whether some prefix of the set holds *ip*.\n def contains?(ip : IP) : ::Bool\n # Removes *prefix*, masked; false when it was not there.\n def delete(prefix : Prefix) : ::Bool\n def empty? : ::Bool\n # Whether *prefix*, masked, is one of the set's - this prefix, not one\n # holding it; `lookup(prefix.first)` asks that.\n def includes?(prefix : Prefix) : ::Bool\n def initialize(prefixes : Array(Prefix))\n def initialize\n # The longest prefix of the set holding *ip*, or nil.\n def lookup(ip : IP) : Prefix | ::Nil\n # Every prefix of the set, IPv4 first, in `Prefix` order.\n def prefixes : Array(Prefix)\n # How many prefixes the set holds.\n def size : Int32\nend\n\nimpl Error for NetipError\n def message : String\nend\n\nimpl Std::Traits::Comparable for IP\n def <=>(other : self) : Int32\nend\n\nimpl Std::Traits::Comparable for Prefix\n def <=>(other : self) : Int32\nend\n" }, { "name": "std/number", "path": "src/std/number.iyi", - "sourceSha256": "b95c5cf83d5f3a2e15e6043b2a21b818ebcc90d66e78edcb9fcb7f7b7ce67040", + "sourceSha256": "57a76f31e6f832afe4ba9e9b890276b61a4178ff6cbca656ce8b93d78748ca6e", "surface": "module std/number\n\npub enum RoundingMode : Int32\n TiesEven = 0\n TiesAway = 1\n ToZero = 2\n ToPositive = 3\n ToNegative = 4\nend\n\nstruct ::Float64\n # To `digits` places in the mode asked for — Crystal's\n # `round(digits, mode: ...)`: `2.675.round(2, :ties_away)` spelled\n # `2.675.round(2, RoundingMode::TiesAway)`. A negative `digits` rounds to\n # tens, hundreds, ..., dividing by the scale first as the prelude's\n # `round(digits)` does.\n # Past a double's range the answer is at hand, as for the prelude's.\n def round(digits : Int32, mode : Std::Number::RoundingMode) : Float64\n def round(mode : Std::Number::RoundingMode) : Float64\nend\n\nstruct ::Number\n def + : self\n def abs : self\n def abs2 : self\n def self.additive_identity : self\n def divmod(number)\n def self.multiplicative_identity : self\n def negative? : ::Bool\n def positive? : ::Bool\n # An integer is already whole: every mode answers self. `Float64` names\n # the mode below; `round` with no arguments is the prelude's, a tie to\n # the even one, which is `TiesEven`.\n def round(mode : Std::Number::RoundingMode = Std::Number::RoundingMode.new(0)) : self\n def sign : Int32\n def self.zero : self\n def zero? : ::Bool\nend\n" }, { "name": "std/number_format", "path": "src/std/number_format.iyi", - "sourceSha256": "a40cfd087457929c1ee319de0306b8d72b2e4ded7dbc359dcf3085f0d5668a5b", + "sourceSha256": "bab228ed53b7dbbdbfd7c190d7d897b52e624b83b1cb0f08ca561bc2fee781dc", "surface": "module std/number_format\n\n# Which binary prefixes `humanize_bytes` writes.\npub enum BinaryPrefixFormat : Int32\n IEC = 0\n JEDEC = 1\nend\n\nstruct ::Int\n # A count of bytes scaled by powers of 1024, as `humanize` scales by\n # 1000.\n def humanize_bytes(precision : Int32 = 3, separator : ::Char = '.', *, significant : ::Bool = true, unit_separator : ::String = \"\", format : Std::NumberFormat::BinaryPrefixFormat = Std::NumberFormat::BinaryPrefixFormat::IEC) : ::String\nend\n\nstruct ::Number\n # The number with its integer digits in groups of *group*, *delimiter*\n # between them and *separator* before the fraction. *decimal_places*\n # rounds to that many places, half to even; nil keeps an integer whole\n # and a float's shortest digits. *only_significant* drops the fraction's\n # trailing zeros, and the separator if nothing is left.\n def format(separator : ::Char = '.', delimiter : ::Char = ',', decimal_places : Int32 | ::Nil = nil, *, group : Int32 = 3, only_significant : ::Bool = false) : ::String\n # The number scaled to an SI prefix, with *precision* significant\n # digits (or, with `significant: false`, places), *unit_separator*\n # between it and the prefix.\n def humanize(precision : Int32 = 3, separator : ::Char = '.', delimiter : ::Char = ',', *, significant : ::Bool = true, unit_separator : ::String = \"\") : ::String\nend\n" }, { @@ -412,91 +412,97 @@ { "name": "std/option_parser", "path": "src/std/option_parser.iyi", - "sourceSha256": "b394a198eeaf8219d011c903348a85a66773c12716dc160c19e20e3714f08cca", + "sourceSha256": "dcb71d0551ad2bfa2a78f9bcceab109b4e5f4e8e2e89432604bcf15d25075ed1", "surface": "module std/option_parser\n\npub class OptionParser\n # What a flag does with the argument after it: `--name VALUE` needs one,\n # `--color [WHEN]` takes one unless it is missing or is a flag itself,\n # and `--verbose` takes none.\n enum Value : Int32\n None = 0\n Required = 1\n Optional = 2\n end\n def banner : String | ::Nil\n # The help's first line, \"options:\" until it is set.\n def banner=(text : String) : Nil\n def help : String\n def initialize\n def invalid_option(&block : (String -> Nil)) : Nil\n def missing_option(&block : (String -> Nil)) : Nil\n # One flag spelled two ways, `-v` and `--verbose`: the value either\n # states, both take.\n def on(short : String, long : String, description : String, &block : (String -> Nil)) : Nil\n # A value is taken by `--name VALUE`, `--name=VALUE`, `-n VALUE` and\n # `-nVALUE`, the four spellings the other library reads. Only the space\n # form was: `--name=VALUE` was stored under the key `--name=VALUE`, which\n # no argument ever looks up, and `-nVALUE` refused itself as not a flag.\n # A short flag is one character, not one byte: `-é` was refused too.\n #\n # A bracketed value, `--color [WHEN]`, `-c [WHEN]` or `-c[WHEN]`, may be\n # left out, as the other library reads the brackets. It was a required\n # value: `--color` last panicked \"flag --color needs a value\", and\n # `--color -v` handed `-v` to `--color` instead of running it.\n def on(flag : String, description : String, &block : (String -> Nil)) : Nil\n def parse(args : Array(String)) : Array(String)\n def parse : Array(String)\n # A line of its own in the help, between the flags and commands\n # registered before it and after it.\n def separator(text : String) : Nil\n def to_s : String\nend\n" }, { "name": "std/otp", "path": "src/std/otp.iyi", - "sourceSha256": "c09b8e1edf7daa57ca7621ad8129b0b638927618a79c0dd14c4019fab15140b7", + "sourceSha256": "9b26764db8f8368f552392c142ee90f42ae9d279360fa5c3b686cbbfe7502024", "surface": "module std/otp\n\npub struct HOTP\n def self.code(secret : Array(UInt8), counter : Int64, digits : Int32 = 6, algorithm : OTPAlgorithm = OTPAlgorithm::SHA1) : String\n def initialize\nend\n\n# The HMAC hash, by the name RFC 6238 and the key URI format give it.\npub enum OTPAlgorithm : Int32\n SHA1 = 0\n SHA256 = 1\n SHA512 = 2\nend\n\n# A key URI: what an authenticator app needs to make the codes a server\n# will check. `counter` is nil for TOTP and the next counter for HOTP;\n# `period` is TOTP's step and 30, unused, for HOTP.\npub struct OTPAuth\n def account : String\n def algorithm : OTPAlgorithm\n def counter : Int64 | ::Nil\n def digits : Int32\n def self.hotp(secret : Array(UInt8), account : String, counter : Int64, issuer : String = \"\", algorithm : OTPAlgorithm = OTPAlgorithm::SHA1, digits : Int32 = 6) : OTPAuth\n def hotp? : Bool\n def initialize(secret : Array(UInt8), account : String, issuer : String, algorithm : OTPAlgorithm, digits : Int32, period : Int32, counter : Int64 | ::Nil)\n def issuer : String\n def self.parse(uri : String) : OTPAuth | OTPError\n def period : Int32\n def secret : Array(UInt8)\n def to_s : String\n # An empty *issuer* is none: the label is the account alone and no\n # `issuer` parameter is written.\n def self.totp(secret : Array(UInt8), account : String, issuer : String = \"\", algorithm : OTPAlgorithm = OTPAlgorithm::SHA1, digits : Int32 = 6, period : Int32 = 30) : OTPAuth\nend\n\n# What `OTPAuth.parse` answers for a URI it cannot read.\npub struct OTPError\n def initialize(message : String)\n def to_s : String\nend\n\npub struct TOTP\n def self.code(secret : Array(UInt8), unix_time : Int64, step : Int32 = 30, digits : Int32 = 6, algorithm : OTPAlgorithm = OTPAlgorithm::SHA1) : String\n def initialize\n # The code for this second by the wall clock.\n def self.now(secret : Array(UInt8), step : Int32 = 30, digits : Int32 = 6, algorithm : OTPAlgorithm = OTPAlgorithm::SHA1) : String\n # Whether *code* is the code for *unix_time*'s step or one of the\n # *window* steps either side of it. A code of the wrong length is false\n # at once; the length of the right one is not a secret.\n def self.verify(secret : Array(UInt8), code : String, unix_time : Int64, window : Int32 = 1, step : Int32 = 30, digits : Int32 = 6, algorithm : OTPAlgorithm = OTPAlgorithm::SHA1) : Bool\nend\n\nimpl Error for OTPError\n def message : String\nend\n" }, { "name": "std/p256", "path": "src/std/p256.iyi", - "sourceSha256": "fff30034b881b76ce3e290fc5952af15bf42391ebd61cd2149e1d7109422d551", + "sourceSha256": "c169b790a4fc66a2d022e75f4af90877c33a9a431902043970c79b213cf64b67", "surface": "module std/p256\n\n# A P-256 private key, and the public key that goes with it.\npub struct P256PrivateKey\n # The key whose d is *bytes*, 32 big-endian, or why it is not one: not\n # 32 bytes, or not in [1, n - 1].\n def self.from_bytes(bytes : String | Array(UInt8)) : P256PrivateKey | EcdsaError\n # A PKCS #8 OneAsymmetricKey (RFC 5958) holding a P-256 key: version 0 or\n # 1, the algorithm id-ecPublicKey with prime256v1, and an ECPrivateKey\n # inside the privateKey OCTET STRING.\n def self.from_pkcs8(der : Array(UInt8)) : P256PrivateKey | EcdsaError\n # SEC 1's ECPrivateKey (RFC 5915): version 1, the 32-byte private key,\n # then optionally the curve ([0], which must be prime256v1) and the\n # public key ([1], which must be the private key's).\n def self.from_sec1(der : Array(UInt8)) : P256PrivateKey | EcdsaError\n def initialize(d : P256Num, public_key : EcdsaPublicKey, public_point : Array(UInt8))\n def public_key : EcdsaPublicKey\n # The public key's point, uncompressed: 0x04, then x and y, 32 bytes each.\n def public_point : Array(UInt8)\n # ECDH: x of [d]Q for the peer's point Q, 32 bytes big-endian - or why\n # *peer* is not a point of P-256 (checked before d is used).\n def shared_secret(peer : Array(UInt8)) : Array(UInt8) | EcdsaError\n # The signature of *message* under *hash*: the DER of Ecdsa-Sig-Value,\n # SEQUENCE { r INTEGER, s INTEGER }, as X.509 and TLS 1.3 carry it. The\n # nonce is RFC 6979's, so the same key and message sign alike.\n def sign(hash : EcdsaHash, message : String | Array(UInt8)) : Array(UInt8)\nend\n" }, { "name": "std/p384", "path": "src/std/p384.iyi", - "sourceSha256": "bceeb1a2e690f13b5ab58010a40173ced4f43829058c224587d921dc643d53c1", + "sourceSha256": "74fa321a5b5b618d357e8a517f5dde6da4f44796e5b39ef9fe39167ca70223c7", "surface": "module std/p384\n\n# A P-384 private key, and the public key that goes with it.\npub struct P384PrivateKey\n # The key whose d is *bytes*, 48 big-endian, or why it is not one: not\n # 48 bytes, or not in [1, n - 1].\n def self.from_bytes(bytes : String | Array(UInt8)) : P384PrivateKey | EcdsaError\n # A PKCS #8 OneAsymmetricKey (RFC 5958) holding a P-384 key: version 0 or\n # 1, the algorithm id-ecPublicKey with secp384r1, and an ECPrivateKey\n # inside the privateKey OCTET STRING.\n def self.from_pkcs8(der : Array(UInt8)) : P384PrivateKey | EcdsaError\n # SEC 1's ECPrivateKey (RFC 5915): version 1, the 48-byte private key,\n # then optionally the curve ([0], which must be secp384r1) and the\n # public key ([1], which must be the private key's).\n def self.from_sec1(der : Array(UInt8)) : P384PrivateKey | EcdsaError\n def initialize(d : P384Num, public_key : EcdsaPublicKey, public_point : Array(UInt8))\n def public_key : EcdsaPublicKey\n # The public key's point, uncompressed: 0x04, then x and y, 48 bytes each.\n def public_point : Array(UInt8)\n # ECDH: x of [d]Q for the peer's point Q, 48 bytes big-endian - or why\n # *peer* is not a point of P-384 (checked before d is used), or why the\n # answer is no secret.\n def shared_secret(peer : Array(UInt8)) : Array(UInt8) | EcdsaError\n # The signature of *message* under *hash*: the DER of Ecdsa-Sig-Value,\n # SEQUENCE { r INTEGER, s INTEGER }, as X.509 and TLS 1.3 carry it. The\n # nonce is RFC 6979's, so the same key and message sign alike.\n def sign(hash : EcdsaHash, message : String | Array(UInt8)) : Array(UInt8)\n # The signature of a message whose digest under *hash* is *digest*, as\n # `sign` makes it - or why *digest* is not one of *hash*'s.\n def sign_digest(hash : EcdsaHash, digest : Array(UInt8)) : Array(UInt8) | EcdsaError\nend\n" }, + { + "name": "std/parallel", + "path": "src/std/parallel.iyi", + "sourceSha256": "fc94240d0df780804b0abb18b8dedc5e3429b0f301400f576f8ea645ca65592b", + "surface": "module std/parallel\n\n# The four verbs, on whatever can say how many elements it has and give\n# the sequence the chunks read (`ParallelRun.source`).\npub trait Parallel\n type Elem\n # The block on every element, in chunks the pool's tasks take one at a\n # time; nil, or the error the block answered for the lowest index that\n # answered one. A chunk is *grain* elements; 0 cuts `PIECES` chunks a\n # worker. The same for the three below.\n def each_parallel(grain : Int32 = 0, &block : (Elem -> R)) : R | ::Nil forall R\n # `map`'s answer, computed in chunks on the pool, in the elements' order.\n def map_parallel(grain : Int32 = 0, &block : (Elem -> U)) : Array(U) forall U\n # The elements folded with the block from *identity*, a chunk at a time.\n def reduce_parallel(identity : Elem, grain : Int32 = 0, &block : (Elem, Elem -> Elem)) : Elem\n # The sum of what the block answers for each element, and its type's\n # zero when there are none.\n def sum_parallel(grain : Int32 = 0, &block : (Elem -> U)) : U forall U\nend\n\n# :nodoc: - the engine the verbs share.\npub class ParallelRun\n # The chunk *at*'s bounds.\n def self.bounds(at : Int64, chunk : Int64, size : Int64) : ::Tuple(Int64, Int64)\n def self.each(source : S, grain : Int32, work : SharedProc(T, R)) : R | ::Nil forall S, T, R\n def self.each_here(source : S, size : Int64, chunk : Int64, work : SharedProc(T, R)) : R | ::Nil forall S, T, R\n # Each task answers the lowest index it saw fail, with its error, or\n # *size* and nothing. `failed_at` is the lowest any task has seen: no\n # chunk starting past it is begun, no element past it is run, and\n # every element before it still is.\n def self.each_tasks(source : S, size : Int64, chunk : Int64, chunks : Int64, tasks : Int32, work : SharedProc(T, R)) : Array(IyiTask(::Tuple(Int64, Array(R)))) forall S, T, R\n def self.fetch(list : List(T), index : Int64) : T forall T\n def self.fetch(range : Range(Int32, Int32), index : Int64) : Int32\n def self.fetch(range : Range(Int64, Int64), index : Int64) : Int64\n def self.map(source : S, grain : Int32, work : SharedProc(T, U)) : Array(U) forall S, T, U\n def self.map_here(source : S, size : Int64, chunk : Int64, work : SharedProc(T, U)) : Array(U) forall S, T, U\n # The tasks, each taking chunks until none is left, and each answering\n # the chunks it ran by number; joined before this answers.\n def self.map_tasks(source : S, size : Int64, chunk : Int64, chunks : Int64, tasks : Int32, work : SharedProc(T, U)) : Array(IyiTask(Array(::Tuple(Int64, Array(U))))) forall S, T, U\n # How the elements are cut: {elements a chunk, chunks, tasks to take\n # them}, and 0 tasks when the caller runs them itself - one worker, one\n # chunk, or a build with no threads.\n def self.plan(size : Int64, grain : Int32) : ::Tuple(Int64, Int64, Int32)\n def self.reduce(source : S, identity : T, grain : Int32, work : SharedProc(::Tuple(T, T), T)) : T forall S, T\n def self.reduce_here(source : S, identity : T, size : Int64, chunk : Int64, work : SharedProc(::Tuple(T, T), T)) : T forall S, T\n def self.reduce_tasks(source : S, identity : T, size : Int64, chunk : Int64, chunks : Int64, tasks : Int32, work : SharedProc(::Tuple(T, T), T)) : Array(IyiTask(Array(::Tuple(Int64, T)))) forall S, T\n def self.size(list : List(T)) : Int64 forall T\n def self.size(range : Range(B, E)) : Int64 forall B, E\n # The sequence the chunks read, which nothing may write while they do.\n def self.source(list : List(T)) : List(T) forall T\n def self.source(array : Array(T)) : List(T) forall T\n def self.source(range : Range(Int32, Int32)) : Range(Int32, Int32)\n def self.source(range : Range(Int64, Int64)) : Range(Int64, Int64)\n # The caller, cancelled while the chunks ran, does not see what they\n # left: it stops here, as a cancelled task joining another does.\n def self.stop_if_cancelled : Nil\n def self.sum(source : S, grain : Int32, work : SharedProc(T, U)) : U forall S, T, U\n def self.sum_here(source : S, size : Int64, chunk : Int64, work : SharedProc(T, U)) : U forall S, T, U\n def self.sum_tasks(source : S, size : Int64, chunk : Int64, chunks : Int64, tasks : Int32, work : SharedProc(T, U)) : Array(IyiTask(Array(::Tuple(Int64, U)))) forall S, T, U\nend\n\nimpl Parallel for Std::List::List(T) forall T\nend\n\nimpl Parallel for Array(T) forall T\nend\n\nimpl Parallel for Range(B, E) forall B, E\nend\n" + }, { "name": "std/password", "path": "src/std/password.iyi", - "sourceSha256": "f29e48291275b0af500b4635f7a1722ac6f425055ff97bb4db16950f5cc4e922", + "sourceSha256": "50ee9c764b7416547f10410e00c5c412414e22abcb69fdcd8fae2000b87760c5", "surface": "module std/password\n\n# bcrypt, Provos and Mazières (USENIX 1999), as OpenBSD's `bcrypt.c`\n# writes it and Crystal's `Crypto::Bcrypt::Password` reads it: a password\n# hashed into a 60-character string that carries its own cost and salt,\n#\n# $2b$11$ 22 characters of salt 31 characters of hash\n#\n# both in bcrypt's own base64 (`./A-Za-z0-9`, no padding). `hash` draws 16\n# bytes of salt from `Random::Secure` and writes `$2b$`; `verify` reads\n# `$2a$`, `$2b$` and `$2y$`, hashes the password again under the salt and\n# cost it finds, and compares the two strings with\n# `Subtle.constant_time_compare`, so how long a wrong guess takes does not\n# say how much of it was right.\n#\n# The password is OpenBSD's key: its bytes up to the first NUL, cut to 72\n# bytes, then a NUL, and only the first 72 bytes of that matter. So a\n# password's 73rd byte onwards changes nothing, and neither does anything\n# after a NUL: \"a\\0b\" hashes as \"a\" does, which is what OpenBSD's C, with\n# its `strlen`, does with it. OpenBSD's `$2a$` once counted the length in a\n# byte and wrapped at 255 (the reason for `$2b$`); this reads `$2a$` as\n# `$2b$`, so a `$2a$` hash that old code made of a password of 255 bytes or\n# more does not verify here.\n#\n# *cost* is the log2 of the number of key-schedule passes: each one more\n# doubles the time. 11 is Crystal's default and this one's; outside 4 to\n# 31 `hash` panics with a sentence. A stored hash is data, and a malformed\n# one, or one whose cost is outside 4 to 31, does not verify: `verify`\n# answers false for it rather than ending the task.\npub struct Bcrypt\n def self.hash(password : String | Array(UInt8), cost : Int32 = 11) : String\n def initialize\n def self.verify(password : String | Array(UInt8), hash : String) : Bool\nend\n\n# scrypt, RFC 7914: PBKDF2-HMAC-SHA256 spread into *p* blocks of `128 * r`\n# bytes, each block walked through a table of *n* versions of itself\n# (ROMix) by BlockMix, a chain of Salsa20/8 cores, and PBKDF2 once more\n# over what comes out. The table is what makes it costly to guess at: a\n# guess needs `128 * r * n` bytes of memory, or the time to recompute\n# what it did not keep. RFC 7914's interactive-login figure is n = 16384,\n# r = 8, p = 1, sixteen mebibytes.\n#\n# *max_memory* is a refusal, not a cap: the parameters ask for\n# `128 * r * p + 128 * r * (n + 2)` bytes, which is how OpenSSL counts\n# them, and past the limit the call panics rather than allocate. Its\n# default, 32 MiB, is OpenSSL's and so Python's, so a call one library\n# accepts the other accepts too. Refused with a sentence besides: an *n*\n# that is not a power of two above 1, or not below 2^(16r) (RFC 7914 §2;\n# OpenSSL refuses it too), an *r*, *p* or length below one, and a\n# `128 * r * p` that does not fit an `Int32` (p * r of 2^24 and up).\npub struct Scrypt\n def self.derive(password : String | Array(UInt8), salt : String | Array(UInt8), n : Int32, r : Int32, p : Int32, length : Int32, max_memory : Int64 = 33554432_i64) : Array(UInt8)\n def initialize\nend\n" }, { "name": "std/path", "path": "src/std/path.iyi", - "sourceSha256": "4dc17d742d5722934c344aa57cd8b82a25cf158a94a900467fc2955745f662ed", + "sourceSha256": "f23959d538f2e27eeff22ba53deee1ce66247f114a073a01bb8af31cf9a0354f", "surface": "module std/path\n\npub struct Path\n enum Kind : UInt8\n POSIX = 0_u8\n WINDOWS = 1_u8\n def self.native : Kind\n # Returns `true` if this enum value equals `POSIX`\n def posix? : Bool\n # Returns `true` if this enum value equals `WINDOWS`\n def windows? : Bool\n end\n def /(part : String | Path) : Path\n def <=>(other : Path) : Int32\n def ==(other : Path) : Bool\n def self.[](first : String | Path, second : String | Path, third : String | Path) : Path\n def self.[](first : String | Path, second : String | Path) : Path\n def self.[](name : String = \"\") : Path\n def self.[](parts : Array(String)) : Path\n # A share is absolute without a separator after it: `\\\\server\\share` is\n # the share's root, as Windows and Python read it. It was relative, so\n # after `Dir.cd` to a share every `expand` panicked \"base is not\n # absolute\".\n def absolute? : Bool\n def anchor : String | ::Nil\n # The last component; the anchor, or the empty name, is its own. A\n # *suffix* found at the end is left off.\n def basename(suffix : String | ::Nil = nil) : String\n def self.compare_strings(s1 : String, s2 : String) : Int32\n # As the two names' `windows_fold`s compare, read here without folding\n # either while both are ASCII.\n def self.compare_strings_ci(s1 : String, s2 : String) : Int32\n # Everything before the last component: `\".\"` for a bare name, the\n # anchor for a single component under it.\n def dirname : String\n def drive : String | ::Nil\n def each_parent(&block : (Path -> Nil)) : Nil\n def each_part(&block : (String -> Nil)) : Nil\n def empty? : Bool\n def ends_with_separator? : Bool\n # Absolute and normalized, under *base* when relative. *base* is\n # `Program.env(\"PWD\")` when not given (on Windows, never), the\n # process's own directory when nothing set that, and a relative path\n # with neither is refused.\n # `home: true` reads a leading `~` from the home directory the\n # platform names (see `home_directory`).\n def expand(base : Path | String | Nil = nil, home : Bool = false) : Path\n # The last `.suffix` of the basename, with its dot; none for a name\n # that starts with its only dot (`.bashrc`) or ends with one (`foo.`).\n def extension : String\n def hash : Int32\n def initialize(name : String = \"\", kind : Kind = Kind.native)\n def inspect(io : IO) : Nil\n # A path of the kind this platform does not use names its constructor,\n # as the other library writes it: `Path.posix(\"/usr/bin\")` on Windows\n # inspected as `Path[\"/usr/bin\"]`, a Windows path, which `==` tells\n # apart from it.\n def inspect : String\n def join(first : String | Path, second : String | Path, third : String | Path) : Path\n def join(first : String | Path, second : String | Path) : Path\n # `self` with *part* under it; a *part* with an anchor of its own is\n # the answer by itself, and an empty one changes nothing. A Windows\n # anchor can be half of one, and then the other half is `self`'s, as\n # ntpath joins and Windows reads the name: a root with no drive is on\n # `self`'s drive, and a drive-relative *part* on `self`'s drive goes\n # under `self`. Answered by itself, `C:\\a` joined with `\\b` was `\\b`,\n # a root on whatever drive is current, and with `C:b` it was `C:b`,\n # where `expand` read both on `C:\\a`.\n def join(part : String) : Path\n def join(part : Path) : Path\n def join(parts : Array(String)) : Path\n def kind : Kind\n def name : String\n def native? : Bool\n def self.new(path : Path) : Path\n # `.` dropped, `..` folded into the component before it (kept at the\n # front of a relative path, dropped under a root), separators collapsed.\n def normalize : Path\n def parent : Path\n # Every ancestor, the outermost first: `\"a/b/c\"` has `\".\"`, `\"a\"`,\n # `\"a/b\"`; `\"/a/b\"` has `\"/\"`, `\"/a\"`. Each is a `parent` of the one\n # after it, so it keeps the spelling `parent` keeps: rebuilt from\n # `parts` and joined with `separator`, `Path.windows(\"C:/a/b/c\")` had\n # `\"C:/a\\\\b\"` where its `parent` is `\"C:/a/b\"`.\n def parents : Array(Path)\n # The anchor, if any, then each non-empty component.\n def parts : Array(String)\n def self.posix(name : String = \"\") : Path\n def posix? : Bool\n def relative? : Bool\n def relative_to(base : Path | String) : Path\n # The path that reaches `self` from *base*, or nil where none can be\n # spelled: different anchors, or a *base* that climbs through `..`.\n def relative_to?(base : Path | String) : Path | ::Nil\n def root : String | ::Nil\n # The anchor with the root a share has whether written or not.\n protected def rooted_anchor : String\n def sibling(name : String | Path) : Path\n def stem : String\n def to_kind(kind : Kind) : Path\n def to_native : Path\n def to_posix : Path\n def to_s(io : IO) : Nil\n def to_s : String\n def to_windows : Path\n def self.windows(name : String = \"\") : Path\n def windows? : Bool\n # *s* as a Windows name compares: `/` written `\\`, ASCII letters lower\n # case, and on Windows every other letter in the case its file systems\n # fold it to - LCMAP_UPPERCASE without linguistic casing, measured to\n # make the same classes as CompareStringOrdinal's on every BMP unit. A\n # surrogate pair stays as written, as both leave it. ASCII alone folded,\n # `C:\\Ä` and `c:\\ä` were two paths where NTFS has one file; built for\n # another platform there is no table to ask, and ASCII alone is folded.\n def self.windows_fold(s : String) : String\nend\n\nimpl Std::Traits::Comparable for Path\n def <=>(other : self) : Int32\nend\n" }, { "name": "std/png", "path": "src/std/png.iyi", - "sourceSha256": "7be25c27293df2ca5e6f59d0e711e6316e23bd675d364534f45511b7c55a3ea9", + "sourceSha256": "20919d5887d367148d507a910c44987539064e7fa0059437e7b63cb063828d58", "surface": "module std/png\n\n# Pixels, row after row from the top, each pixel's samples in `layout`\n# order; a sample is one byte at depth 8, two (big-endian, as PNG stores\n# them) at depth 16. `transparent`, for a Gray or RGB image, is the one\n# color that is to be read as transparent (a gray value, or red, green and\n# blue), in the image's own depth.\npub class Image\n def channels : Int32\n def self.channels_of(layout : Layout) : Int32\n def depth : Int32\n def height : Int32\n def initialize(width : Int32, height : Int32, layout : Layout, depth : Int32, samples : String, transparent : Array(Int32) | ::Nil = nil)\n def layout : Layout\n def self.layout_name(layout : Layout) : String\n # Sample *c* of the pixel at column *x*, row *y*: 0 to 255, or to 65535\n # at depth 16. Outside the image, a panic.\n def sample(x : Int32, y : Int32, c : Int32) : Int32\n def samples : String\n # Four bytes a pixel, red, green, blue and alpha. A gray sample is all\n # three colors; a pixel of the transparent color has alpha 0, any other\n # without an alpha sample 255; a 16-bit sample is its high byte.\n def to_rgba8 : String\n def transparent : Array(Int32) | ::Nil\n def width : Int32\nend\n\n# Which samples a pixel has, in the order they are stored.\npub enum Layout : Int32\n Gray = 0\n GrayAlpha = 1\n RGB = 2\n RGBA = 3\nend\n\npub struct Png\n def self.assemble(width : Int32, height : Int32, depth : Int32, ctype : Int32, interlace : Bool, plte : String, trns : String, texts : Array(::Tuple(String, String)), filtered : String) : String\n def self.chunk(io : String::Builder, name : String, data : String) : Nil\n # The file in *data*, decoded; or what is wrong with it. A file whose\n # IHDR claims more than *max_pixels* pixels is refused before anything\n # is allocated for them.\n def self.decode(data : String, max_pixels : Int64 = MAX_PIXELS) : PngFile | PngError\n # *image* as a PNG file. *texts* are written as tEXt chunks, keyword\n # and text, or as iTXt where the text is not Latin-1.\n def self.encode(image : Image, interlace : Bool = false, texts : Array(::Tuple(String, String)) = [] of ::Tuple(String, String)) : String\n # A palette image: *indices* one byte a pixel, row after row, each an\n # entry of *palette*, whose entries are red, green, blue and alpha. The\n # bit depth is the smallest of 1, 2, 4 and 8 the palette fits; tRNS is\n # written up to the last entry that is not opaque.\n def self.encode_indexed(width : Int32, height : Int32, indices : String, palette : Array(::Tuple(Int32, Int32, Int32, Int32)), interlace : Bool = false, texts : Array(::Tuple(String, String)) = [] of ::Tuple(String, String)) : String\n # A zlib stream in a metadata chunk, inflated to at most MAX_METADATA.\n def self.inflate_metadata(stream : String, name : String) : String | PngError\n def initialize\n # The image data inflated into exactly the bytes IHDR promised, the\n # filters undone, and the pixels laid out.\n def self.pixels(d : PngDecode, data : String, idats : Array(::Tuple(Int32, Int32))) : Image | PngError\n # tEXt, zTXt or iTXt (sections 11.3.3.2-4).\n def self.read_text(name : String, p : Pointer(UInt8), body : Int32, len : Int32) : PngText | PngError\n # A keyword is 1 to 79 Latin-1 printable bytes, no space at either end\n # and no two together (section 11.3.3.2): anything else is a panic.\n def self.text_chunk(keyword : String, text : String) : ::Tuple(String, String)\nend\n\n# What is wrong with a PNG file: `message` names the chunk, and its offset\n# in the file where that helps.\npub struct PngError\n def initialize(message : String)\n def to_s : String\nend\n\n# A decoded file: the image and what the file said about it.\npub class PngFile\n def bit_depth : Int32\n # IHDR's color type (0, 2, 3, 4 or 6) and bit depth, as written.\n def color_type : Int32\n # gAMA's value, the gamma times 100,000; nil without one.\n def gamma : Int32 | ::Nil\n # iCCP's profile name and the profile inflated; nil without one.\n def icc_name : String | ::Nil\n def icc_profile : String | ::Nil\n def image : Image\n def initialize(image : Image, color_type : Int32, bit_depth : Int32, interlaced : Bool, palette : String, transparency : String, gamma : Int32 | ::Nil, srgb_intent : Int32 | ::Nil, icc_name : String | ::Nil, icc_profile : String | ::Nil, texts : Array(PngText), skipped : Array(String))\n def interlaced : Bool\n # PLTE's entries, red, green and blue a byte each; \"\" without one.\n def palette : String\n # The ancillary chunks skipped, by name, in file order.\n def skipped : Array(String)\n # sRGB's rendering intent, 0 to 3; nil without one.\n def srgb_intent : Int32 | ::Nil\n def texts : Array(PngText)\n # tRNS as written: an alpha byte per palette entry, or the transparent\n # gray or RGB sample(s) as two bytes each; \"\" without one.\n def transparency : String\nend\n\n# A tEXt, zTXt or iTXt chunk: `chunk` says which. `language` and\n# `translated_keyword` are iTXt's, \"\" for the other two.\npub class PngText\n def chunk : String\n def initialize(chunk : String, keyword : String, text : String, language : String = \"\", translated_keyword : String = \"\")\n def keyword : String\n def language : String\n def text : String\n def translated_keyword : String\nend\n\nimpl Error for PngError\n def message : String\nend\n" }, { "name": "std/process", "path": "src/std/process.iyi", - "sourceSha256": "afad6c79111206b02bfb15419101ae82c131cdabc51d6e268de0c20eb0e3da14", + "sourceSha256": "5c40c07b1e28f457575a1048916da99c7de894be1ecdfd37889e402027f9a8e5", "surface": "module std/process\n\n# The child's standard input, as a pipe this program writes.\npub class ChildInput\n # Closes the pipe: the child reads the end of its input.\n def close : Nil\n def initialize(child : ProcessChild)\n # Writes all of *text*, parking while the pipe is full. A child that\n # closed its input, or ended, is `ChildInputClosed`; `close` has made it\n # so too.\n def write(text : String) : Nil | ChildInputClosed | Cancelled\nend\n\n# A write to a child that had closed its input - or ended.\npub struct ChildInputClosed\n def initialize(pid : Int64)\n def pid : Int64\nend\n\n# One of the child's output streams, as a pipe this program reads. Each\n# read parks until the child has written, or its end of the pipe closed.\npub class ChildOutput\n def initialize(child : ProcessChild, output : ::Bool)\n # What the child has written that this program has not read, up to\n # *max* bytes, once there is some: nil at the end.\n def read(max : Int32 = 65536) : String | ::Nil | Cancelled\n # Everything the child writes from here to its end.\n def read_all : String | Cancelled\n # The next line, without its `\\n` (and a `\\r` before it) unless *chomp*\n # is false; the last one need not end in one. nil at the end.\n def read_line(chomp : ::Bool = true) : String | ::Nil | Cancelled\nend\n\n# A child started by `Process.spawn`, running beside this program.\npub class ChildProcess\n # Whether the child has ended, without waiting; once it has, `status`\n # says how.\n def ended? : ::Bool\n def initialize(child : ProcessChild, input : ::Bool, output : ::Bool, error : ::Bool)\n # Ends the child: SIGKILL on Linux and darwin, and on Windows the child\n # and every program it started. Nothing once it has ended.\n def kill : Nil\n # The child's process id, which the operating system may give another\n # process once this one has ended and been waited for.\n def pid : Int64\n # Sends *number* to the child, Linux and darwin. Windows has no signals,\n # and a call there panics: `terminate` and `kill` are what it has.\n def signal(number : Int32) : Nil\n # How the child ended, once it has; nil while it runs.\n def status : ProcessStatus | ::Nil\n # The child's standard error, asked for as `Redirect::Pipe`.\n def stderr : ChildOutput\n # The child's standard input, asked for as `Redirect::Pipe`.\n def stdin : ChildInput\n # The child's standard output, asked for as `Redirect::Pipe`.\n def stdout : ChildOutput\n # Asks the child to stop: SIGTERM on Linux and darwin, which it may\n # handle. Windows has no request to stop a program, and ends it and what\n # it started, as `kill` does. Nothing once it has ended.\n def terminate : Nil\n # Parks until the child has ended and answers how; at once if it had.\n # Its input, if a pipe, is closed first: a child reading to the end of\n # its input would otherwise wait for this program while this program\n # waits for it.\n def wait : ProcessStatus | Cancelled\nend\n\npub struct Process\n # Replaces this program with *command*, and answers only if it could not:\n # a `ProcessError`, with this program as it was, in the directory it was.\n #\n # On Linux and darwin the process becomes the program - the same pid,\n # this program's three standard streams and none of its other\n # descriptors (on Linux from 5.11, which marks them for the exec) - and\n # nothing after the call runs. Windows cannot replace a program: there\n # the program runs on this program's streams, this program waits for it\n # and ends with its exit code, and a pid is not kept.\n #\n # *command*, *args*, *chdir* and *env* are `run`'s. What this program had\n # written to its own buffered streams is flushed first.\n def self.exec(command : String, args : Array(String) = [] of String, chdir : String | ::Nil = nil, env : Hash(String, String) | Hash(String, String | ::Nil) | Hash(String, Nil) | Nil = nil) : ProcessError\n # This program's own executable, as a path; nil where the platform will\n # not say.\n def self.executable : String | ::Nil\n def initialize\n # This program's own process id.\n def self.pid : Int64\n # Runs *command* with *args* and answers once it has ended.\n #\n # *command* is a path, or a name looked for on `PATH` (on Windows also as\n # `NAME.exe` and `NAME.com`; a `.bat` or `.cmd` is refused). Each of\n # *args* reaches the child as one argument, and no shell reads them.\n # *input* is written to the child's standard input, and without it the\n # child reads nothing. *chdir* is the directory it runs in. *env* changes\n # the child's environment alone: a name given a value is set, a name\n # given `nil` removed. With *capture* false the child writes to this\n # program's own streams and the result's texts are empty.\n #\n # Answers a `ProcessResult` for a child that ran, whatever its exit code;\n # a `ProcessError` when it could not be started; and `Cancelled`, after\n # the child is ended, when the task running this is cancelled. Nothing\n # blocks the thread: the other tasks run while the child does.\n def self.run(command : String, args : Array(String) = [] of String, input : String | ::Nil = nil, chdir : String | ::Nil = nil, capture : ::Bool = true, env : Hash(String, String) | Hash(String, String | ::Nil) | Hash(String, Nil) | Nil = nil) : ProcessResult | ProcessError | Cancelled\n # Starts *command* and answers at once with the running child, which\n # this program then talks to and waits for: `ChildProcess`.\n #\n # *command*, *args*, *chdir* and *env* are `run`'s. Each of *input*,\n # *output* and *error* is a `Redirect`: `Pipe` to write the child's input\n # or read its output through the handle, `Inherit` to share this\n # program's own stream (which is flushed first), `Null` for the null\n # device. All three are inherited unless asked.\n #\n # Answers a `ProcessError` when the child could not be started.\n #\n # The child runs until it ends or is ended; `wait` collects its end and\n # is what a program owes every child it starts. A pipe nobody reads fills\n # and stops the child that writes it, so read what is asked for as a\n # pipe - on two tasks at once for both output and errors - before or\n # while waiting. A task cancelled in any of the handle's calls ends the\n # child before it answers `Cancelled`, as `run` does: a task that stops\n # leaves no program running.\n def self.spawn(command : String, args : Array(String) = [] of String, input : Redirect = Redirect::Inherit, output : Redirect = Redirect::Inherit, error : Redirect = Redirect::Inherit, chdir : String | ::Nil = nil, env : Hash(String, String) | Hash(String, String | ::Nil) | Hash(String, Nil) | Nil = nil) : ChildProcess | ProcessError\nend\n\n# A program that could not be started, and the platform's error number for\n# why. `directory` is set when it was the directory it was to run in.\npub struct ProcessError\n def code : Int32\n def command : String\n def denied? : ::Bool\n def directory : String | ::Nil\n def initialize(command : String, code : Int32, directory : String | ::Nil = nil)\n # Nothing by that name: not on PATH, no such file, no such directory.\n def not_found? : ::Bool\n def reason : String\nend\n\n# What a program that ran came back with. `exit_code` is the child's own;\n# on Linux and darwin a child ended by a signal has `signal` set and the\n# exit code a shell would report for it, 128 plus the signal. On Windows\n# `signal` is always 0 and a crash is its exception code, `0xC0000005` for\n# an access violation, read as an Int32.\npub struct ProcessResult\n def exit_code : Int32\n def initialize(exit_code : Int32, signal : Int32, stdout : String, stderr : String)\n def signal : Int32\n def stderr : String\n def stdout : String\n def success? : ::Bool\nend\n\n# How a child ended: `exit_code`, and on Linux and darwin the `signal`\n# that ended it, as `ProcessResult` says them.\npub struct ProcessStatus\n def exit_code : Int32\n def initialize(exit_code : Int32, signal : Int32)\n def signal : Int32\n def success? : ::Bool\nend\n\n# How one of a spawned child's standard streams is connected.\npub enum Redirect : Int32\n Pipe = 0\n Inherit = 1\n Null = 2\nend\n\nimpl Error for ProcessError\n def message : String\nend\n\nimpl Error for ChildInputClosed\n def message : String\nend\n" }, { "name": "std/random", "path": "src/std/random.iyi", - "sourceSha256": "63c494faac054e6f96737ebb73e0edcaeaba7222a07d15355db09ef154091f0c", + "sourceSha256": "86c85a4d3c05e929fdaa2c9055b93092ac941b3fd21747a41e1c1dad6ff78b2d", "surface": "module std/random\n\npub class Random\n # The operating system's randomness, for anything a guess must not reach:\n # tokens, keys, nonces, query IDs. It is a `Random`, so `rand(n)`,\n # `rand(range)`, `hex` and every other draw take their values from it, and\n # `Random::Secure.rand(n)` answers without an instance. There is no state to\n # seed: each draw is a fresh request to the kernel (or libSystem, or the\n # Windows or WASI host), and nothing is held between draws, so a `fork`\n # cannot hand the child the parent's next value.\n class Secure < ::Std::Random::Random\n # *count* bytes at *buffer*, every one from the platform's entropy source,\n # or a panic that says why there are none: a byte that only looks random\n # is never the answer.\n def self.fill(buffer : Pointer(UInt8), count : Int32) : Nil\n # The generator fields a `Random` carries, which nothing here reads.\n def initialize\n def next_u : UInt32\n # The whole buffer in one request rather than four bytes at a time.\n def random_bytes(buffer : Array(UInt8)) : Nil\n end\n # *n* random bytes in base64 with `+` `/` and `=` padding, no line breaks.\n def base64(n : Int32 = 16) : String\n def self.base64(n : Int32 = 16) : String\n # *n* random bytes as two lowercase hex digits each.\n def hex(n : Int32 = 16) : String\n def self.hex(n : Int32 = 16) : String\n # The stream came from the seed, and one seed was one stream: the other\n # library takes the stream apart from the seed, zero unless asked, and\n # the same seed gave two different sequences in the two libraries.\n def initialize(seed : UInt64, sequence : UInt64 = 0_u64)\n # Any integer seed and sequence, as the other library takes them. There\n # were two overloads, an `Int32` and an `Int64` seed with a `UInt64`\n # sequence, beside this class's own `UInt64` pair: `Random.new(7, 3)`,\n # the form the comment above writes, cast its literals to all three and\n # was an ambiguous call, and a `UInt32` seed matched none of them. A\n # negative seed or sequence panics in the conversion, as it raises there.\n def self.new(seed : ::Int, sequence : ::Int = 0_u64) : Random\n def self.new : Random\n def next_bool : Bool\n def self.next_bool : Bool\n # 2^53 values, the high draw first: two 32-bit draws make the integer,\n # and it is divided by 2^53 - 1, so both ends can come out.\n def next_float : Float64\n def next_u : UInt32\n # The same draws without a generator in hand, from one seeded the first\n # time it is asked, as the other library's `Random.rand` draws from its\n # default. `Random::Secure.rand(...)` and the rest are these, answered by\n # the operating system instead.\n def self.next_u : UInt32\n def rand(n : Int32) : Int32\n def rand(n : UInt32) : UInt32\n def rand(n : Int64) : Int64\n def rand(n : UInt64) : UInt64\n # In `[0, x)`: 2^53 evenly spaced values, scaled. Not `rand * x`, which\n # divides by 2^53 - 1 and can answer *x* itself.\n def rand(x : Float64) : Float64\n # The span is taken as the unsigned difference, so a range across zero\n # does not overflow on the way. The whole of `Int32` is one draw read as\n # an `Int32`, as the other library reads it, rather than added to the\n # bottom of the range, which would answer the same value with its top\n # bit turned over.\n def rand(range : Range(Int32, Int32)) : Int32\n # As the `Int32` range: the whole of `Int64` is 2^64 values, one more than\n # a `UInt64` span can count, and is two draws, high first, read as one.\n def rand(range : Range(Int64, Int64)) : Int64\n # An end left out is the other library's choice too: `rand(x)` cannot\n # reach *x*, and an inclusive range scales `rand`, which can.\n def rand(range : Range(Float64, Float64)) : Float64\n def rand : Float64\n def self.rand(n : Int32) : Int32\n def self.rand(n : UInt32) : UInt32\n def self.rand(n : Int64) : Int64\n def self.rand(n : UInt64) : UInt64\n def self.rand(x : Float64) : Float64\n def self.rand(range : Range(Int32, Int32)) : Int32\n def self.rand(range : Range(Int64, Int64)) : Int64\n def self.rand(range : Range(Float64, Float64)) : Float64\n def self.rand : Float64\n # Four bytes a draw, low byte first, the last draw cut short: a seeded\n # generator fills a buffer with the other library's bytes.\n #\n # The bytes are an `Array(UInt8)`, as `UUID#bytes` and std/digest's\n # `hash_bytes` are, rather than the other library's `Bytes`: std/slice\n # reaches this module through std/indexable's `sample`, and an import\n # back would be a cycle.\n def random_bytes(buffer : Array(UInt8)) : Nil\n def random_bytes(n : Int32 = 16) : Array(UInt8)\n def self.random_bytes(buffer : Array(UInt8)) : Nil\n def self.random_bytes(n : Int32 = 16) : Array(UInt8)\n # The bytes as a String, which is how std/base64 takes them.\n protected def self.text_of(bytes : Array(UInt8)) : String\n # *n* random bytes in the URL-safe alphabet, `-` and `_`; unpadded unless\n # asked, as the other library answers it, since a token in a URL or a\n # file name has no use for the `=`.\n def urlsafe_base64(n : Int32 = 16, padding : Bool = false) : String\n def self.urlsafe_base64(n : Int32 = 16, padding : Bool = false) : String\nend\n\nclass ::Array(T)\n def sample(random : Std::Random::Random | ::Nil = nil) : T\n # *n* distinct elements. One is `sample` in a list; more are a reservoir\n # (each element past the first *n* replaces a slot with probability\n # n/(i+1)) shuffled at the end - both as the other library does it.\n def sample(n : Int32, random : Std::Random::Random | ::Nil = nil) : Array(T)\n def shuffle(random : Std::Random::Random | ::Nil = nil) : Array(T)\n # Fisher-Yates from the back, `rand(i + 1)` a step: the other library's\n # `shuffle!`, so a seeded generator puts the elements where it would.\n def shuffle_in_place(random : Std::Random::Random | ::Nil = nil) : Array(T)\nend\n\nstruct ::StaticArray(T, N)\n def initialize\n # `Array#shuffle_in_place` for the array that holds its elements inline:\n # the other library's `StaticArray#shuffle!`, the same draws in the same\n # order, so a seeded generator puts the elements where it would. Its\n # sample of noise does exactly this to a 256-entry permutation table.\n def shuffle_in_place(random : Std::Random::Random | ::Nil = nil) : self\nend\n" }, { "name": "std/reference_storage", "path": "src/std/reference_storage.iyi", - "sourceSha256": "eeee691e8ebd184a7e654c2ee616aaee68d6d5d193de9c25853395a5a9bc5957", + "sourceSha256": "b047e709ee67ee8d747c01aad8046dd869ac1e30f46b8526a404ccbad4fa355f", "surface": "module std/reference_storage\n\npub struct ReferenceStorage(T) < Value\n # Field-wise, with class fields compared by identity. `T#==` on a\n # `String` field follows the pointer, so uninitialized storage died of\n # a memory fault; two storages with equal string contents at different\n # addresses compared equal while `hash` mixed the pointer bytes. Whole-\n # blob compare is also wrong: `other` is passed by value and padding\n # bytes are not copied, so two constructed `Node`s compared unequal.\n def ==(other : ReferenceStorage(T)) : Bool\n def ==(other : Object) : Bool\n # Field by field, exactly as `==` compares: a class field by its\n # identity, any other by its own `hash`, mixed with a wrapping step\n # (checked `h * 31` panicked once `instance_sizeof(T)` passed a few\n # bytes). Mixing the raw bytes disagreed with `==`: a `Float64` field\n # holding `0.0` and one holding `-0.0` compare equal and differ in their\n # sign bit, and a `String?` field compares by content while its bytes\n # are the pointer, so equal storages hashed apart.\n def hash : Int32\n def initialize\n def inspect : String\n def to_reference : T\n def to_s : String\nend\n\nclass ::Reference\n def self.pre_initialize(address : Pointer)\n def self.unsafe_construct(address : Pointer, *args, **opts) : self\nend\n" }, { "name": "std/regex", "path": "src/std/regex.iyi", - "sourceSha256": "a606bf1b00791cd897309faf04248074e1efafa47f1c7cb5e72538e2321fed35", + "sourceSha256": "172a184987ebe364aed92606428e63c78eb346f741d5a28e51cdab88bd524067", "surface": "module std/regex\n\npub struct Regex\n # One match: the text it was found in and where each group began and\n # ended, in bytes. A group that took no part in the match - the second\n # of `(a)|(b)` when `a` matched, `(x)?` when `x` did not - has no span,\n # and `[]?` answers nil for it.\n class MatchData\n # The text group *n* matched. A group the pattern does not have, or one\n # that took no part in this match, is a mistake the program made, and\n # it panics with the reason.\n def [](n : Int32) : String\n # The text the group called *name* matched; a name the pattern does not\n # have, or a group that took no part, panics with the reason.\n def [](name : String) : String\n # The text group *n* matched, or nil for a group that took no part or\n # that the pattern does not have.\n def []?(n : Int32) : String | ::Nil\n # The text the group called *name* matched, or nil for a group that\n # took no part or a name the pattern does not have.\n def []?(name : String) : String | ::Nil\n # Where group *n* begins, in characters, as the other library counts:\n # `\"クリスタル\".match(/リ(ス)/).begin(0)` is 1 where `byte_begin` is 3.\n def begin(n : Int32 = 0) : Int32\n # Where group *n* begins, in bytes: what `String#[]` and `index` count\n # in. A negative *n* counts from the last group.\n def byte_begin(n : Int32 = 0) : Int32\n # Where group *n* ends, in bytes.\n def byte_end(n : Int32 = 0) : Int32\n # What each unnamed group matched, in order, nil for one that took no\n # part; the named ones are `named_captures`, as in the other library.\n def captures : Array(String | ::Nil)\n # Where group *n* ends, in characters.\n def end(n : Int32 = 0) : Int32\n # *replacement* with its references filled from this match: `\\0` to\n # `\\9` are groups, `\\k` a named one, `\\\\` one backslash, and any\n # other `\\` is itself - how the other library reads the replacement\n # `String#sub` and `gsub` are given. A group that took no part is\n # empty; a name the pattern does not have panics.\n def expand(replacement : String) : String\n # How many capture groups the pattern has, the whole match not counted.\n def group_size : Int32\n def initialize(regex : Regex, string : String, slots : Array(Int32))\n # `Regex::MatchData(\"ab\" 1:\"a\" name:\"b\")`, as the other library writes it.\n def inspect : String\n # What each named group matched, nil for one that took no part.\n def named_captures : Hash(String, String | ::Nil)\n # The text after the match.\n def post_match : String\n # The text before the match.\n def pre_match : String\n # The pattern that matched.\n def regex : Regex\n # How many groups there are, the whole match counted: `(a)(b)` is 3.\n def size : Int32\n # The text the match was found in.\n def string : String\n # Every group, the whole match first.\n def to_a : Array(String | ::Nil)\n # The text the whole pattern matched.\n def to_s : String\n end\n # The flags a whole pattern is compiled with, combined with `|`. The\n # numbers are the other library's, which is what lets a literal's flags\n # (`/a/i`, `/a/mx`) reach this type unchanged.\n enum Options : Int32\n IGNORE_CASE = 1\n MULTILINE = 6\n DOTALL = 2\n MULTILINE_ONLY = 4\n EXTENDED = 8\n end\n # The same pattern with the same flags.\n def ==(other : Regex) : Bool\n # Whether the pattern occurs anywhere in *text*: what `case text when\n # /x/` asks.\n def ===(text : String) : Bool\n # Where the first match begins, in bytes, or nil. It sets nothing: the\n # other library's `=~` also fills `$~`, which iyi does not have, and a\n # program that wants the groups asks `match`.\n def =~(text : String) : Int32 | ::Nil\n # Where the first match at or after byte *offset* begins, in bytes, as\n # `String#byte_index` answers for a string.\n def byte_index(text : String, offset : Int32 = 0) : Int32 | ::Nil\n # How many capture groups the pattern has.\n def capture_count : Int32\n # The machine is compiled first either way: it is what refuses a pattern\n # this engine does not take, and the fast paths only ever see one it did.\n # They read the pattern's text as written, so a pattern with flags, which\n # change what that text means, is the machine's alone. So is one holding\n # U+FFFD: the machine reads a byte that begins no character as that\n # character, and comparing bytes would find only its three-byte spelling.\n def self.compile(pattern : String, options : Options = Options::None) : Regex\n # Every non-overlapping match with its groups, leftmost first, as `scan`\n # finds them.\n def each_match(text : String, & : (MatchData -> Nil)) : Nil\n # *text* with every character a pattern would read as an operator\n # escaped, so `Regex.compile(Regex.escape(text))` matches *text* and\n # nothing else - with `x` too, whose whitespace and `#` are escaped.\n def self.escape(text : String) : String\n def find(text : String) : String | ::Nil\n # The number of the group called *name*, or nil.\n def group_index(name : String) : Int32 | ::Nil\n # The name of group *n*, or nil for an unnamed group or one the pattern\n # does not have.\n def group_name(n : Int32) : String | ::Nil\n def initialize(nfa : RxNfa, literal : String | ::Nil, runs : RxRuns | ::Nil, source : String, options : Options, names : List(String | ::Nil))\n # The pattern as a literal would write it, `/a\\/b/ix`.\n def inspect : String\n # The first match at or after byte *pos*, with its groups; a negative\n # *pos* counts from the end. `^` and `\\A` still mean the start of the\n # text, not *pos*.\n def match(text : String, pos : Int32 = 0) : MatchData | ::Nil\n # Whether the pattern spans the whole of *text*. The other library's\n # `match?` asks whether it occurs anywhere, which is `===` and\n # `String#matches?` here.\n def match?(text : String) : Bool\n # The match that begins exactly at byte *pos*, with its groups, or nil:\n # the other library's `match_at_byte_index` with `MatchOptions::ANCHORED`,\n # Python's `pattern.match(text, pos)`, what a scanner asks at its\n # offset. As there, the text before *pos* is still the text: `^` and\n # `\\A` mean its start, not *pos*, and `\\b` sees the byte before *pos*.\n # A negative *pos* counts from the end, as `match`'s does.\n def match_at(text : String, pos : Int32) : MatchData | ::Nil\n # Each named group's number and name.\n def name_table : Hash(Int32, String)\n # The other library's name for `compile`.\n def self.new(pattern : String, options : Options = Options::None) : Regex\n # The flags the pattern was compiled with; inline ones are the\n # pattern's own text and are not here.\n def options : Options\n # Every match replaced, into one buffer: `out = out + ...` copied all of\n # the text so far at each match, and eleven substitutions over a 60 MB\n # sequence with a million matches each did not finish. The replacement\n # is taken as written; `String#gsub` is the one that reads `\\1` in it.\n def replace(text : String, replacement : String) : String\n # Every match replaced by what the block answers for it — the per-match\n # substitution (`gsub(pattern, hash)` in the other library) that a fixed\n # replacement cannot say.\n def replace(text : String, & : (String -> String)) : String\n def scan(text : String) : Array(String)\n # The pattern as written, the other library's `Regex#source`: what a\n # literal was compiled from, for a program that prints its patterns.\n def source : String\n def split(text : String) : Array(String)\n # The pattern with its flags inside it, `(?i-msx:a+)`, which compiles to\n # a pattern that matches as this one does: what `union` joins.\n def to_s : String\n # One pattern matching any of *patterns*: a string as its own text, a\n # `Regex` with its flags kept to itself (`(?i-msx:...)`, its `to_s`).\n # The groups of every pattern after the first are numbered after the\n # groups before them.\n def self.union(patterns : Array(T)) : Regex forall T\n def self.union(*patterns : String | Regex) : Regex\nend\n\nclass ::String\n def =~(pattern : Std::Regex::Regex) : Int32 | ::Nil\n def gsub(pattern : Std::Regex::Regex, replacement : String) : String\n def gsub(pattern : Std::Regex::Regex, & : (String, Std::Regex::Regex::MatchData -> String)) : String\n # Each match replaced by its entry in *table*; a match the table has no\n # entry for is dropped, as the other library drops it.\n def gsub(pattern : Std::Regex::Regex, table : Hash(String, String)) : String\n def index(pattern : Std::Regex::Regex, offset : Int32 = 0) : Int32 | ::Nil\n def match(pattern : Std::Regex::Regex, pos : Int32 = 0) : Std::Regex::Regex::MatchData | ::Nil\n def matches?(pattern : Std::Regex::Regex) : Bool\n def scan(pattern : Std::Regex::Regex) : Array(Std::Regex::Regex::MatchData)\n def scan(pattern : Std::Regex::Regex, & : (Std::Regex::Regex::MatchData -> Nil)) : String\n def sub(pattern : Std::Regex::Regex, replacement : String) : String\n def sub(pattern : Std::Regex::Regex, & : (String, Std::Regex::Regex::MatchData -> String)) : String\nend\n" }, { "name": "std/router", "path": "src/std/router.iyi", - "sourceSha256": "46616dc94ec6b99be5edc5ff896bba1aec8f66faaa5c65587d76d9fec69c9647", + "sourceSha256": "61e0deea649f0870a719a8f386af043a0394dd381514967b3511bc0a441034ac", "surface": "module std/router\n\n# A route's pattern, `[METHOD ][HOST]/[PATH]`, parsed: what `Router#handle`\n# registers. `match` reads a request path's wildcards; `relation` and\n# `conflict` are the comparisons that decide precedence and refuse two\n# patterns neither of which is more specific.\npub class Pattern\n # Two relations of parts (methods, then each segment) as the relation\n # of the whole: a superset in one part and a subset in another is\n # neither, an overlap.\n def self.combine(first : Int32, second : Int32) : Int32\n # The sentence refusing this pattern beside *other*, nil when the two\n # can be registered together: when they have the same host and match\n # the same requests, or some requests in common with neither more\n # specific. It names both and a request both match.\n def conflict(other : Pattern) : String | ::Nil\n # Whether this pattern matched *pieces* without its trailing wildcard\n # taking anything: `/x/` on `/x/`, not on `/x/y`. What decides whether\n # `/x` is redirected to `/x/`.\n def exact?(pieces : Array(String)) : Bool\n def host : String\n # A wildcard's name: an ASCII letter or `_`, then letters, digits, `_`.\n def self.identifier?(name : String) : Bool\n def initialize(text : String, method : String, host : String, kinds : Array(Int32), names : Array(String))\n protected def kinds : List(Int32)\n # The wildcards' values when *path* - a request path, rooted and clean,\n # still percent-encoded - matches this pattern's path; nil when it does\n # not. The method and host are not looked at.\n def match(path : String) : Hash(String, String) | ::Nil\n # `match` on a path already cut into `pieces`.\n def match_pieces(pieces : Array(String)) : Hash(String, String) | ::Nil\n # Whether a request of *method* is one this pattern's method takes.\n def matches_method?(method : String) : Bool\n def method : String\n # Whether this pattern matches a strict subset of *other*'s requests,\n # methods and paths together; hosts are not compared.\n def more_specific?(other : Pattern) : Bool\n protected def names : List(String)\n # *text* read as a pattern, or why it is not one: an invalid method, no\n # `/` to start the path, a `{` in the host, a path a request's cleaned\n # path never equals (`//`, `.`, `..`), a wildcard that is not a whole\n # segment, `{$}` or `{name...}` before the end, a name that is not an\n # identifier or is used twice, a malformed percent escape.\n def self.parse(text : String) : Pattern | RouterError\n # *path*'s segments after its first `/`, each percent-decoded: `/` is\n # one empty piece, and a trailing slash is a last empty one. Nil for a\n # path that is not rooted or holds a malformed escape. Decoded a segment\n # at a time, so `%2F` is inside a segment, never between two.\n def self.pieces(path : String) : Array(String) | ::Nil\n # How this pattern's requests relate to *other*'s, methods and paths\n # together: `EQUIVALENT`, `MORE_GENERAL`, `MORE_SPECIFIC`, `DISJOINT` or\n # `OVERLAPS`.\n def relation(other : Pattern) : Int32\n # The pattern as it was written.\n def to_s : String\nend\n\n# Requests sent to handlers by pattern, Go 1.22's `ServeMux`:\n#\n# router = Router.new\n# router.handle(\"GET /users/{id}\") { |req, params| Response.new(200, params[\"id\"]) }\n# router.handle(\"/static/\") { |req, params| FileServer.serve(root, req) }\n# Server.serve(listener, router, workers: 1)\n#\n# A `Router` is written as it is set up and holds procs nobody checked,\n# so it is not `Share`: it is served on one thread (`workers: 1`), as the\n# handler or from a block that calls it, and refused at the argument on\n# more than one worker; `SharedRouter` below routes on every worker.\n#\n# `handle` panics on a pattern that is not one, on one that conflicts with\n# a pattern already registered, and after the router has answered a\n# request. `call` answers with the most specific pattern's handler, or a\n# 307 (to the cleaned path, or to a subtree's `/`), a 405 with `Allow`, a\n# 400 for a malformed escape, a 404.\npub class Router\n # The answer to *req*: its handler's, or the router's own 307, 400, 404\n # or 405.\n def call(req : Request) : Response\n # *path* - rooted - with `.` and empty segments dropped and `..` taking\n # the one before it off, its trailing slash kept: Go's `cleanPath`.\n def self.clean(path : String) : String\n # Sends the requests *pattern* matches to the block, with the request\n # and the wildcards' values by name.\n def handle(pattern : String, &handler : (Request, Hash(String, String) -> Response)) : Nil\n # A `Host` value without its port, as Go's `stripHostPort` has it: an\n # IPv6 address in brackets loses the brackets with the port.\n def self.host_of(value : String) : String\n def initialize\n # *pattern* parsed, panicking on text that is not a pattern and on one\n # that conflicts with one of *known*: what `handle` registers, here and\n # in `SharedRouter`.\n def self.parse_against(pattern : String, known : P) : Pattern forall P\n # The patterns registered, in the order they were.\n def patterns : Array(Pattern)\n def self.plain(status : Int32, text : String) : Response\n def self.redirect(path : String, query : String | ::Nil) : Response\n # Which of *patterns* - a `Router`'s or a `SharedRouter`'s - takes *req*:\n # its index and the wildcards' values, or the router's own 307, 400, 404\n # or 405.\n def self.route(patterns : P, req : Request) : ::Tuple(Int32, Hash(String, String)) | Response forall P\nend\n\n# Why a pattern is refused: `Pattern.parse`'s answer for text that is not\n# a pattern. `Router#handle` panics with its `message`.\npub struct RouterError\n def initialize(reason : String)\n def reason : String\n def to_s : String\nend\n\n# `Router`'s table for `Server.serve` on every worker, which route at\n# once (SPEC.md III.4.13):\n#\n# routes = SharedRouter.new\n# .handle(\"GET /users/{id}\") { |req, params| Response.new(200, params[\"id\"]) }\n# .handle(\"/static/\") { |req, params| FileServer.serve(root, req) }\n# Server.serve(listener, routes)\n#\n# The same patterns, precedence, conflicts and answers as `Router`'s, by\n# the same code (`Router.route`). What differs is what a handler may\n# capture: `handle` is `@[Share]`, so each block is checked where it is\n# written as a parallel task's is - every value it captures `Share`, a\n# pool behind a `Mutex`, a count in an `Atomic` - and kept as a\n# `SharedProc`. A `Router` holds procs nobody checked, so `Server.serve`\n# takes one under `workers: 1` alone. A `SharedRouter` is never written:\n# `handle` answers a new router with the route added, so the one being\n# served is the table it was when the server was given it, with no\n# `handle` after the first request to refuse.\npub class SharedRouter\n # The answer to *req*: its handler's, or the router's own 307, 400, 404\n # or 405.\n def call(req : Request) : Response\n # This router with the requests *pattern* matches sent to the block,\n # with the request and the wildcards' values by name. Panics as\n # `Router#handle` does on a pattern that is not one or that conflicts.\n def handle(pattern : String, &handler : (Request, Hash(String, String) -> Response)) : SharedRouter\n def initialize(patterns : List(Pattern) = List(Pattern).new([] of Pattern), handlers : List(SharedProc(::Tuple(Request, Hash(String, String)), Response)) = List(SharedProc(::Tuple(Request, Hash(String, String)), Response)).new([] of SharedProc(::Tuple(Request, Hash(String, String)), Response)))\n # The patterns registered, in the order they were.\n def patterns : Array(Pattern)\nend\n\nimpl Error for RouterError\n def message : String\nend\n" }, { "name": "std/rsa", "path": "src/std/rsa.iyi", - "sourceSha256": "25ebe39e70765207e03dfb418ac251c7d7f3ecd0f3d419baf428add7cbc354bd", + "sourceSha256": "64732f8d8bd7ed6f9b201b275098e8b2ab8dbf30ba8fe0a38d27d07a07ddaee2", "surface": "module std/rsa\n\n# What reading a key answers for bytes that are not an RSA key.\npub struct RsaError\n def initialize(message : String)\n def to_s : String\nend\n\n# The hashes a signature is made over.\npub enum RsaHash : Int32\n SHA1 = 0\n SHA256 = 1\n SHA384 = 2\n SHA512 = 3\n def digest(data : Array(UInt8)) : Array(UInt8)\n # The DER of a DigestInfo up to the digest itself (RFC 8017 section 9.2,\n # note 1): the algorithm's OID with NULL parameters, then the OCTET\n # STRING's header.\n def digest_info_prefix : Array(UInt8)\n def size : Int32\nend\n\n# An RSA private key with its CRT parameters (RFC 8017 section 3.2, the\n# first representation and the second together), which signs.\npub struct RsaPrivateKey\n # The modulus's length in bits, and in bytes: a signature's length.\n def bits : Int32\n # The one key in a PEM text: an `RSA PRIVATE KEY` block (PKCS #1) or a\n # `PRIVATE KEY` block (PKCS #8).\n def self.from_pem(text : String) : RsaPrivateKey | RsaError\n # An RSAPrivateKey (appendix A.1.2) of two primes: version 0 and the\n # eight numbers.\n def self.from_pkcs1(der : Array(UInt8)) : RsaPrivateKey | RsaError\n # A PKCS #8 PrivateKeyInfo or OneAsymmetricKey (RFC 5958) naming\n # rsaEncryption, its privateKey an RSAPrivateKey. Attributes are passed\n # over; a public key, which version 1 may carry, must be the key's own.\n def self.from_pkcs8(der : Array(UInt8)) : RsaPrivateKey | RsaError\n def initialize(public_key : RsaPublicKey, p : BigInt, q : BigInt, dp : BigInt, dq : BigInt, qi : BigInt, prf : Array(UInt8))\n # The key from its eight numbers, each big-endian - n, e, d, p, q, dp,\n # dq, qi - or why they are not one.\n def self.new(modulus : Array(UInt8), exponent : Array(UInt8), d : Array(UInt8), p : Array(UInt8), q : Array(UInt8), dp : Array(UInt8), dq : Array(UInt8), qi : Array(UInt8)) : RsaPrivateKey | RsaError\n def public_key : RsaPublicKey\n # RSASSA-PKCS1-v1_5 (section 8.2.1) of *message* under *hash*: 0x00\n # 0x01, the 0xFF padding, 0x00, the hash's DigestInfo and the digest.\n def sign_pkcs1v15(hash : RsaHash, message : Array(UInt8) | String) : Array(UInt8)\n # RSASSA-PSS (section 8.1.1) of *message* under *hash*, MGF1 over the\n # same hash: EMSA-PSS-ENCODE (section 9.1.1) with a salt as long as the\n # digest, drawn as the module's header says.\n def sign_pss(hash : RsaHash, message : Array(UInt8) | String) : Array(UInt8)\n def size : Int32\nend\n\npub struct RsaPublicKey\n # The modulus's length in bits, and in bytes: a signature's length.\n def bits : Int32\n def exponent : BigInt\n # An RSAPublicKey (RFC 8017 appendix A.1.1): SEQUENCE { modulus, exponent }.\n def self.from_pkcs1(der : Array(UInt8)) : RsaPublicKey | RsaError\n # A SubjectPublicKeyInfo (RFC 5280 section 4.1.2.7) naming rsaEncryption.\n def self.from_spki(der : Array(UInt8)) : RsaPublicKey | RsaError\n def self.from_spki(value : DerValue) : RsaPublicKey | RsaError\n def initialize(modulus : BigInt, exponent : BigInt, bits : Int32)\n def modulus : BigInt\n # The key from a modulus and an exponent, each big-endian.\n def self.new(modulus : Array(UInt8), exponent : Array(UInt8)) : RsaPublicKey | RsaError\n def size : Int32\n # Whether *signature* is the PKCS #1 v1.5 signature (RSASSA-PKCS1-v1_5,\n # section 8.2.2) of *message* under *hash*.\n def verify_pkcs1v15(hash : RsaHash, message : Array(UInt8) | String, signature : Array(UInt8)) : Bool\n # Whether *signature* is an RSASSA-PSS signature (section 8.1.2) of\n # *message* under *hash*, MGF1 over the same hash, with a salt of\n # *salt_length* bytes (the digest's length when nil).\n def verify_pss(hash : RsaHash, message : Array(UInt8) | String, signature : Array(UInt8), salt_length : Int32 | ::Nil = nil) : Bool\nend\n\nimpl Error for RsaError\n def message : String\nend\n" }, { "name": "std/secure_dns", "path": "src/std/secure_dns.iyi", - "sourceSha256": "2ec99a10b574c9ffc740e71924e0db2bf74a7e3c884b2f69ca400f132d1acccf", + "sourceSha256": "40a8d8321c79083e1dffa4a3cfba10084bc271681a02248ae502ae27635f0305", "surface": "module std/secure_dns\n\n# DNS over HTTPS to one URL: see the module's header.\npub class DnsOverHttps\n # The message a response carries: a 200 of `application/dns-message`\n # (parameters after it allowed, the type in any case) of at most\n # `MAX_ANSWER` bytes. RFC 8484 4.2.1 calls any 2xx a success, but no\n # other 2xx carries an answer to a query: 204 has no body and 206 a part\n # of one.\n def self.answer(response : Response, label : String) : Bytes | DnsError | Cancelled\n # RFC 8484 4.1's `dns` parameter: base64url (RFC 4648 5) \"with padding\n # characters removed\".\n def self.base64url(message : String) : String\n def close : Nil\n # Connections the HTTP client has opened, and requests sent over one it\n # kept.\n def connections_made : Int32\n def connections_reused : Int32\n def exchange(queries : Array(Bytes), timeout_ms : Int32) : Array(Bytes) | DnsError | Cancelled\n # *url* is `https://`: an `http://` upstream would be plaintext with\n # extra steps, and is refused. *address*, when given, is where the URL's\n # host is, and nothing is asked to find it. *config* holds the roots the\n # server must chain to and the ALPN offered: by default the system's\n # roots, and `h2` before `http/1.1`.\n def initialize(url : String, address : String | ::Nil = nil, config : TlsConfig | ::Nil = nil, method : String = \"POST\")\n def label : String\n # \"POST\" or \"GET\".\n def method : String\n def url : String\n def zero_id? : Bool\nend\n\n# DNS over TLS to one server: see the module's header.\npub class DnsOverTls\n # The address connected to, IPv4 or IPv6.\n def address : String\n def close : Nil\n def config : TlsConfig\n # Connections this upstream has opened: what a program reads to see its\n # connection kept.\n def connections_made : Int32\n def exchange(queries : Array(Bytes), timeout_ms : Int32) : Array(Bytes) | DnsError | Cancelled\n def idle_ms : Int32\n # *name* is what the server's certificate is checked for and *address*\n # where it is; *config* holds the roots it must chain to (the system's\n # by default) and the versions spoken. A name that is not a host name,\n # or an address that is not one, is the program's mistake and refused\n # here.\n def initialize(name : String, address : String, port : Int32 = PORT, config : TlsConfig | ::Nil = nil, idle_ms : Int32 = IDLE_MS)\n def label : String\n # The authentication domain name: what the certificate must carry.\n def name : String\n def port : Int32\n def zero_id? : Bool\nend\n\nimpl Std::Socket::DnsUpstream for DnsOverTls\nend\n\nimpl Std::Socket::DnsUpstream for DnsOverHttps\nend\n" }, { "name": "std/semantic_version", "path": "src/std/semantic_version.iyi", - "sourceSha256": "32b113295e8a61d4d6a9c9387b07f521b684f7127f9ee97ef2ac6dfeb8968ff7", + "sourceSha256": "ae0f1608246ff1e37e047d4b9f20b9388eb9a3ad1ac340b3448a38ec2ce4dfe4", "surface": "module std/semantic_version\n\npub struct SemanticVersion\n # The dot-separated identifiers after `-`; a numeric identifier is held\n # as a number so that `beta.2 < beta.11`, and one too long for an Int32\n # is held as its digits and still ordered as a number.\n struct Prerelease\n # `Prerelease`, not `self`: spelled `self` it was the very method the\n # `impl Comparable` below defines, which replaced it, and that one's\n # `self <=> other` then called itself until the stack ran out.\n def <=>(other : Prerelease) : Int32\n def <=>(other : self) : Int32\n def ==(other : Prerelease) : Bool\n def empty? : Bool\n def hash : Int32\n def identifiers : Array(String | Int32)\n def initialize(identifiers : Array(String | Int32) = [] of String | Int32)\n def self.parse(str : String) : self\n def to_s(io : IO) : Nil\n def to_s : String\n end\n # Precedence, with build metadata ignored as the standard says. The\n # parameter is `SemanticVersion` for the reason `Prerelease#<=>` gives:\n # as `self` the `impl Comparable` replaced this, and every `<=>`, `==`,\n # `<` and sort recursed until the stack ran out (a hang under --release).\n def <=>(other : SemanticVersion) : Int32\n # Identity, with build metadata read: `1.0.0+a` is not `1.0.0+b`.\n def ==(other : SemanticVersion) : Bool\n def build : String | ::Nil\n def bump_major : self\n def bump_minor : self\n # A prerelease of a patch is bumped to that patch; a release to the next.\n def bump_patch : self\n def copy_with(major : Int32 = @major, minor : Int32 = @minor, patch : Int32 = @patch, prerelease : String | Prerelease | Nil = @prerelease, build : String | ::Nil = @build) : SemanticVersion\n def hash : Int32\n def initialize(major : Int32, minor : Int32, patch : Int32, prerelease : String | Prerelease | Nil = nil, build : String | ::Nil = nil)\n def major : Int32\n def minor : Int32\n def self.parse(str : String) : self\n def self.parse?(str : String) : self | ::Nil\n def patch : Int32\n def prerelease : Prerelease\n def to_s(io : IO) : Nil\n def to_s : String\n def self.valid?(str : String) : Bool\nend\n\nimpl Std::Traits::Comparable for SemanticVersion::Prerelease\n def <=>(other : self) : Int32\nend\n\nimpl Std::Traits::Comparable for SemanticVersion\n def <=>(other : self) : Int32\nend\n" }, { @@ -508,49 +514,49 @@ { "name": "std/shared_proc", "path": "src/std/shared_proc.iyi", - "sourceSha256": "a6654265e51523b44a081f94d9eb1b5603fa0b419697e1b45c5f1a7e90161949", + "sourceSha256": "74a3b3878e026d5cd88923212961c95270be210eaa4d7024f716950b98c74822", "surface": "module std/shared_proc\n\npub struct SharedProc(A, R)\n def call(argument : A) : R\n # The block, checked as a parallel task's block is, as a value any\n # thread may call.\n def self.of(&block : (A -> R)) : SharedProc(A, R)\nend\n" }, { "name": "std/shlex", "path": "src/std/shlex.iyi", - "sourceSha256": "b42d6ad9063539b09f7026ae3f12512628b927e2813b6272b3e3f9a24e6e740c", + "sourceSha256": "b8d3c6f8713495ba9ec90b85073b148c8bb338a3c4c11c5005eacd689adceef2", "surface": "module std/shlex\n\n# POSIX shell words: `split`, `quote` and `join`, as Python's `shlex`.\npub struct Shlex\n def initialize\n # The arguments quoted and joined with spaces: a command line that\n # `split` and `sh` read back as *args*.\n def self.join(args : Array(String)) : String\n # *arg* as one shell word: as it is when every byte is a letter, a\n # digit or one of `%+,-./:=@_`, `''` when empty, else single-quoted.\n def self.quote(arg : String) : String\n # The words of *text*, quotes and backslashes taken off as `sh` takes\n # them; with *comments*, `#` to the end of the line is skipped.\n def self.split(text : String, comments : ::Bool = false) : Array(String) | ShlexError\nend\n\n# Why a text does not split: an unclosed quote or a final backslash.\n# `offset` is the byte where the quote or the backslash stands.\npub struct ShlexError\n def initialize(message : String, offset : Int32)\n def offset : Int32\n def to_s : String\nend\n\nimpl Error for ShlexError\n def message : String\nend\n" }, { "name": "std/signal", "path": "src/std/signal.iyi", - "sourceSha256": "c2d35f9fe0952237ddd8df4c87b2c63520198d9bb1c71a3d76df2ec1779fc748", + "sourceSha256": "41721f5ed5c57f6e8ff66af546c9aa8c313ce6f42e6a895029a4cf6ef786bbc5", "surface": "module std/signal\n\npub enum Signal : Int32\n HUP = 1\n INT = 2\n QUIT = 3\n PIPE = 13\n ALRM = 14\n TERM = 15\n WINCH = 28\n USR1 = 10\n USR2 = 12\n CHLD = 17\n # Runs the block for each arrival of one of *signals*, in this task,\n # until the task is cancelled, and answers `Cancelled` then. Arrivals\n # while the block runs are kept, each signal once.\n def self.each(*signals : Signal, &block : (Signal -> _)) : Cancelled\n # Sets each of *signals* to be ignored by the process: its arrivals are\n # dropped, and one that was pending is forgotten. Windows can ignore\n # Ctrl-C (`INT`) alone; anything else is refused there.\n def self.ignore(*signals : Signal) : Nil\n # Gives each of *signals* its default action back: INT and TERM end the\n # process again, WINCH is ignored again. Windows: `INT` alone.\n def self.reset(*signals : Signal) : Nil\n # Parks until one of *signals* arrives; answers it.\n def self.wait(*signals : Signal) : Signal | Cancelled\nend\n" }, { "name": "std/slice", "path": "src/std/slice.iyi", - "sourceSha256": "1dda673c9cdfccf23aa89b30d80c0499132891714a3bf5c669a79e9ab35dcb7d", - "surface": "module std/slice\n\n# `pub`, because an alias is part of a module's surface like anything else: an\n# unexported one is unreachable from a consumer, which R-2 now says out loud.\npub alias Bytes = Slice(UInt8)\n\npub struct Slice(T)\n # Returns a subslice starting *offset* elements in, extending to the end.\n def +(offset : Int32) : Slice(T)\n # Returns true if sizes match and all elements are equal.\n #\n # A slice of its own element type, as `Array#==` takes an `Array(T)`. It\n # took any `Slice(U)`, and numbers that compare equal across types hash\n # apart: `Slice[-1, -2]` of `Int32` was `==` to the same of `Int64` with\n # hashes 204952928 and -1576659145, so a table missed one by the other.\n def ==(other : Slice(T)) : Bool\n def ==(other : Object) : Bool\n # Returns a subslice of *count* elements starting at *start*.\n def [](start : Int32, count : Int32) : Slice(T)\n # Element access with bounds checking. A negative index counts from the end,\n # so `s[-1]` is the last element; the message names the index as typed rather\n # than the wrapped one, and words it exactly as `Array#[]` and\n # `Indexable#[]` do — the same mistake reads the same way in all three.\n def [](index : Int32) : T\n # Returns a subslice covering *range*.\n def [](range : Range(Int32, Int32)) : Slice(T)\n # Element assignment with bounds checking and write-protection check.\n def []=(index : Int32, value : T) : T\n # Returns a subslice of *count* elements starting at *start*, or nil if out of bounds.\n def []?(start : Int32, count : Int32) : Slice(T) | ::Nil\n # Element access with bounds checking, returning nil if out of bounds.\n def []?(index : Int32) : T | ::Nil\n # Returns a subslice covering *range*, or nil if out of bounds.\n #\n # A range that ends before it begins is empty, not out of bounds, as the\n # other library counts it: `s[3..1]` is the empty slice at 3. The count is\n # taken only once the end is known to be past the start, and the\n # inclusive end is added only while the count can still fit, so an end\n # near the top of `Int32` answers nil instead of overflowing.\n def []?(range : Range(Int32, Int32)) : Slice(T) | ::Nil\n # Returns the total size in bytes of this slice's elements.\n def bytesize : Int32\n # Returns a copy of this slice.\n def clone : Slice(T)\n # Copies *count* elements from raw *source* pointer into this slice.\n def copy_from(source : Pointer(T), count : Int32) : Nil\n # Copies all elements from *source* slice into this slice.\n def copy_from(source : Slice(T)) : Nil\n # Copies *count* elements from this slice to raw *target* pointer.\n def copy_to(target : Pointer(T), count : Int32) : Nil\n # Copies all elements from this slice to *target* slice.\n def copy_to(target : Slice(T)) : Nil\n # Returns a new slice with copied contents. The copy is filled before it\n # takes this slice's read-only flag: `copy_from` into a read-only copy\n # refused, so a read-only slice could not be duplicated at all.\n def dup : Slice(T)\n # Yields each element and its index to the block.\n def each_with_index(& : (T, Int32 -> Nil)) : Nil\n # Returns an empty slice of size 0.\n def self.empty : self\n # Returns true if the slice contains no elements.\n def empty? : Bool\n # Fills *count* elements starting at *start* with *value*.\n def fill(value : T, start : Int32, count : Int32) : self\n # Fills the entire slice with *value*.\n def fill(value : T) : self\n # Fills the entire slice by yielding each index to the block.\n def fill(& : (Int32 -> T)) : self\n # Returns a lowercase hex string representation of Bytes.\n def hexstring : String\n # Creates a slice wrapping *pointer* bounded by *size*.\n #\n # `read_only` is named in this and every `Slice.new`, as in the other\n # library. Positional, it took the value's place when `T` is `Bool`:\n # `Slice(Bool).new(3, true)` was `Slice[false, false, false]`, read-only.\n def initialize(pointer : Pointer(T), size : Int32, *, read_only : Bool = false)\n def inspect : String\n # Moves *count* elements from *source* pointer into this slice, safely handling overlaps.\n def move_from(source : Pointer(T), count : Int32) : Nil\n # Moves all elements from *source* slice into this slice, safely handling overlaps.\n def move_from(source : Slice(T)) : Nil\n # Moves *count* elements from this slice into *target* pointer, safely handling overlaps.\n def move_to(target : Pointer(T), count : Int32) : Nil\n # Moves all elements from this slice into *target* slice, safely handling overlaps.\n def move_to(target : Slice(T)) : Nil\n # Allocates a slice of *size* elements initialized to *value*.\n #\n # These three answer `Slice(T)`, not `self`, as `Array.new` does: when the\n # element type is left to the value or the block, `self` is still the\n # unbound `Slice(T)` while the body builds `Slice(Int32)`, so\n # `Slice.new(3) { |i| i + 1 }` and `Slice.new(3, 7)` did not compile.\n def self.new(size : Int32, value : T, *, read_only : Bool = false) : Slice(T)\n # Allocates a slice of *size* elements on the heap.\n def self.new(size : Int32, *, read_only : Bool = false) : self\n # Allocates a slice of *size* elements initialized by yielding each index.\n def self.new(size : Int32, & : (Int32 -> T)) : Slice(T)\n def self.new(size : Int32, *, read_only : Bool, & : (Int32 -> T)) : Slice(T)\n def read_only? : Bool\n # Reverses the elements of the slice in-place.\n def reverse : self\n def reverse_in_place : self\n # Returns true if this slice and *other* point to the exact same memory and have the same size.\n def same?(other : Slice(T)) : Bool\n def size : Int32\n # Swaps elements at index *i* and index *j*.\n def swap(i : Int32, j : Int32) : self\n # Returns an Array containing all elements in this slice.\n def to_a : Array(T)\n # String representation: Bytes[...] for Slice(UInt8), Slice[...] for others.\n def to_s : String\n # Raw pointer to the underlying buffer.\n def to_unsafe : Pointer(T)\n # Raw element access without bounds checks (required for Indexable).\n def unsafe_fetch(index : Int32) : T\n # Raw element write without bounds checks.\n def unsafe_put(index : Int32, value : T) : T\nend\n\n# The lazy half, which `Slice` had no way into: `std/iterator`'s header says\n# a concrete collection provides its own iterator constructor and names this\n# type twice as one that should, and then the only route from a slice to a\n# lazy chain was `fs.to_a` — copying the whole sequence, which is the cost\n# the tower exists to avoid.\n#\n# A class, as `ArrayIterator` is (std/iterator): an adaptor keeps its source\n# by value, and a struct source was copied into it, so `it.first(2).to_a`\n# and then `it.to_a` gave [1, 2] and [1, 2, 3, 4] where an array's iterator\n# gives [1, 2] and [3, 4].\npub class SliceIterator(T)\n def initialize(slice : Slice(T))\n def next : T | Stop\nend\n\nimpl Std::Indexable::Indexable for Slice(T) forall T\nend\n\nimpl Std::Enumerable::Enumerable for Slice(T) forall T\n def each(& : (T -> Nil)) : Nil\nend\n\nimpl Std::Iterator::Iterator for SliceIterator(T) forall T\nend\n" + "sourceSha256": "19cbd017a0bb4d00081dfee7d2a78ce44221e60ea15662d33642d9149423d376", + "surface": "module std/slice\n\n# `pub`, because an alias is part of a module's surface like anything else: an\n# unexported one is unreachable from a consumer, which R-2 now says out loud.\npub alias Bytes = Slice(UInt8)\n\npub struct Slice(T)\n # Returns a subslice starting *offset* elements in, extending to the end.\n def +(offset : Int32) : Slice(T)\n # Returns true if sizes match and all elements are equal.\n #\n # A slice of its own element type, as `Array#==` takes an `Array(T)`. It\n # took any `Slice(U)`, and numbers that compare equal across types hash\n # apart: `Slice[-1, -2]` of `Int32` was `==` to the same of `Int64` with\n # hashes 204952928 and -1576659145, so a table missed one by the other.\n def ==(other : Slice(T)) : Bool\n def ==(other : Object) : Bool\n # Returns a subslice of *count* elements starting at *start*.\n def [](start : Int32, count : Int32) : Slice(T)\n # Element access with bounds checking. A negative index counts from the end,\n # so `s[-1]` is the last element; the message names the index as typed rather\n # than the wrapped one, and words it exactly as `Array#[]` and\n # `Indexable#[]` do — the same mistake reads the same way in all three.\n def [](index : Int32) : T\n # Returns a subslice covering *range*.\n def [](range : Range(Int32, Int32)) : Slice(T)\n # Element assignment with bounds checking and write-protection check.\n def []=(index : Int32, value : T) : T\n # Returns a subslice of *count* elements starting at *start*, or nil if out of bounds.\n def []?(start : Int32, count : Int32) : Slice(T) | ::Nil\n # Element access with bounds checking, returning nil if out of bounds.\n def []?(index : Int32) : T | ::Nil\n # Returns a subslice covering *range*, or nil if out of bounds.\n #\n # A range that ends before it begins is empty, not out of bounds, as the\n # other library counts it: `s[3..1]` is the empty slice at 3. The count is\n # taken only once the end is known to be past the start, and the\n # inclusive end is added only while the count can still fit, so an end\n # near the top of `Int32` answers nil instead of overflowing.\n def []?(range : Range(Int32, Int32)) : Slice(T) | ::Nil\n # Returns the total size in bytes of this slice's elements.\n def bytesize : Int32\n # Returns a copy of this slice.\n def clone : Slice(T)\n # Copies *count* elements from raw *source* pointer into this slice.\n def copy_from(source : Pointer(T), count : Int32) : Nil\n # Copies all elements from *source* slice into this slice.\n def copy_from(source : Slice(T)) : Nil\n # Copies *count* elements from this slice to raw *target* pointer.\n def copy_to(target : Pointer(T), count : Int32) : Nil\n # Copies all elements from this slice to *target* slice.\n def copy_to(target : Slice(T)) : Nil\n # Returns a new slice with copied contents. The copy is filled before it\n # takes this slice's read-only flag: `copy_from` into a read-only copy\n # refused, so a read-only slice could not be duplicated at all.\n def dup : Slice(T)\n # Yields each element and its index to the block.\n def each_with_index(& : (T, Int32 -> Nil)) : Nil\n # Returns an empty slice of size 0.\n def self.empty : self\n # Returns true if the slice contains no elements.\n def empty? : Bool\n # Fills *count* elements starting at *start* with *value*.\n def fill(value : T, start : Int32, count : Int32) : self\n # Fills the entire slice with *value*.\n def fill(value : T) : self\n # Fills the entire slice by yielding each index to the block.\n def fill(& : (Int32 -> T)) : self\n # Returns a lowercase hex string representation of Bytes.\n def hexstring : String\n # Creates a slice wrapping *pointer* bounded by *size*.\n #\n # `read_only` is named in this and every `Slice.new`, as in the other\n # library. Positional, it took the value's place when `T` is `Bool`:\n # `Slice(Bool).new(3, true)` was `Slice[false, false, false]`, read-only.\n def initialize(pointer : Pointer(T), size : Int32, *, read_only : Bool = false)\n def inspect : String\n # Moves *count* elements from *source* pointer into this slice, safely handling overlaps.\n def move_from(source : Pointer(T), count : Int32) : Nil\n # Moves all elements from *source* slice into this slice, safely handling overlaps.\n def move_from(source : Slice(T)) : Nil\n # Moves *count* elements from this slice into *target* pointer, safely handling overlaps.\n def move_to(target : Pointer(T), count : Int32) : Nil\n # Moves all elements from this slice into *target* slice, safely handling overlaps.\n def move_to(target : Slice(T)) : Nil\n # Allocates a slice of *size* elements initialized to *value*.\n #\n # These three answer `Slice(T)`, not `self`, as `Array.new` does: when the\n # element type is left to the value or the block, `self` is still the\n # unbound `Slice(T)` while the body builds `Slice(Int32)`, so\n # `Slice.new(3) { |i| i + 1 }` and `Slice.new(3, 7)` did not compile.\n def self.new(size : Int32, value : T, *, read_only : Bool = false) : Slice(T)\n # Allocates a slice of *size* elements on the heap.\n def self.new(size : Int32, *, read_only : Bool = false) : self\n # Allocates a slice of *size* elements initialized by yielding each index.\n def self.new(size : Int32, & : (Int32 -> T)) : Slice(T)\n def self.new(size : Int32, *, read_only : Bool, & : (Int32 -> T)) : Slice(T)\n # *text*'s bytes, read-only, without a copy: `Bytes.of(\"GET\")`. A string\n # is never written once it is made (it is `Share` by that), so the view is\n # read-only, and it is what a module that does not handle raw memory reads\n # a string's bytes through, where `Bytes.new(text.to_unsafe, ...)` needs\n # the module marked `@[Unsafe]` (SPEC.md III.13).\n def self.of(text : String) : Slice(UInt8)\n def read_only? : Bool\n # Reverses the elements of the slice in-place.\n def reverse : self\n def reverse_in_place : self\n # Returns true if this slice and *other* point to the exact same memory and have the same size.\n def same?(other : Slice(T)) : Bool\n def size : Int32\n # Swaps elements at index *i* and index *j*.\n def swap(i : Int32, j : Int32) : self\n # Returns an Array containing all elements in this slice.\n def to_a : Array(T)\n # String representation: Bytes[...] for Slice(UInt8), Slice[...] for others.\n def to_s : String\n # Raw pointer to the underlying buffer.\n def to_unsafe : Pointer(T)\n # Raw element access without bounds checks (required for Indexable).\n def unsafe_fetch(index : Int32) : T\n # Raw element write without bounds checks.\n def unsafe_put(index : Int32, value : T) : T\nend\n\n# The lazy half, which `Slice` had no way into: `std/iterator`'s header says\n# a concrete collection provides its own iterator constructor and names this\n# type twice as one that should, and then the only route from a slice to a\n# lazy chain was `fs.to_a` — copying the whole sequence, which is the cost\n# the tower exists to avoid.\n#\n# A class, as `ArrayIterator` is (std/iterator): an adaptor keeps its source\n# by value, and a struct source was copied into it, so `it.first(2).to_a`\n# and then `it.to_a` gave [1, 2] and [1, 2, 3, 4] where an array's iterator\n# gives [1, 2] and [3, 4].\npub class SliceIterator(T)\n def initialize(slice : Slice(T))\n def next : T | Stop\nend\n\nimpl Std::Indexable::Indexable for Slice(T) forall T\nend\n\nimpl Std::Enumerable::Enumerable for Slice(T) forall T\n def each(& : (T -> Nil)) : Nil\nend\n\nimpl Std::Iterator::Iterator for SliceIterator(T) forall T\nend\n\nclass ::IyiIO\n # Writes *bytes*, all of them.\n def write(bytes : Std::Slice::Slice(UInt8)) : Nil\nend\n\nclass ::String\n # A string holding a copy of *bytes*, as they are: what a module turns\n # bytes it read into text with, where `String.new(n) { |buffer| ... }`\n # hands it a `Pointer` (SPEC.md III.13).\n def self.new(bytes : Std::Slice::Slice(UInt8)) : String\nend\n" }, { "name": "std/smtp", "path": "src/std/smtp.iyi", - "sourceSha256": "2d705943fb25cab029ae3af971d812a1df1c514695545e451fa4e99eea77500c", + "sourceSha256": "3bebfcd67402514e080ffde76d979d9c34d0d51046066f6f99751aa6faab37a6", "surface": "module std/smtp\n\npub enum SmtpAuth : Int32\n Plain = 0\n Login = 1\n CramMd5 = 2\nend\n\n# A session with a mail server.\npub class SmtpClient\n def self.ascii?(text : String) : Bool\n # AUTH as *user* with *password*, by *mechanism*, or by the first the\n # server offers of PLAIN, LOGIN and CRAM-MD5.\n def auth(user : String, password : String, mechanism : SmtpAuth | ::Nil = nil) : SmtpError | ::Nil\n # Whether a password may go over a connection without TLS.\n def cleartext_auth : Bool\n # Whether a password may go over a connection without TLS.\n def cleartext_auth=(value : Bool) : Bool\n # The connection closed, without QUIT.\n def close : Nil\n def closed? : Bool\n # *line* sent, and its reply.\n def command(line : String) : SmtpReply | SmtpError\n # A session with *host* on *port*: connected, greeted, EHLO said, and\n # under TLS as *security* asks.\n def self.connect(host : String, port : Int32, security : SmtpSecurity = SmtpSecurity::StartTls, config : TlsConfig = TlsConfig.new, helo : String | ::Nil = nil, timeout_ms : Int32 = TIMEOUT_MS) : SmtpClient | SmtpError\n def self.digit?(b : UInt8) : Bool\n # What EHLO named, by its keyword in upper case: each extension's\n # parameters (\"\" when none). Empty after HELO.\n def extensions : Hash(String, String)\n # The server's 220.\n def greeting : SmtpReply\n def initialize(socket : IyiSocket, host : String, helo : String, timeout_ms : Int32)\n # `[192.0.2.1]`, or `[IPv6:2001:db8::1]` (section 4.1.3).\n def self.literal(address : String) : String\n def self.mechanism_name(mechanism : SmtpAuth) : String\n protected def open(security : SmtpSecurity, config : TlsConfig) : SmtpError | ::Nil\n # QUIT, its 221 read, and the connection closed.\n def quit : SmtpError | ::Nil\n # The next reply, every line of it.\n def reply : SmtpReply | SmtpError\n # *data*, a whole message, from *sender* to *recipients*; *utf8* when\n # the message is RFC 6532's, as SMTPUTF8 carries. *data* is sent as it\n # is, so a message written with `Mail::Message#to_s` must have had its\n # Date to get here. No recipients, or an address that holds a line break\n # or an angle bracket, panics (`SMTP: a message with no recipients`,\n # `SMTP: ... is not an address to put in angle brackets`); every other\n # failure is the `SmtpError` returned.\n def send(sender : String, recipients : Array(String), data : String, utf8 : Bool = false) : SmtpError | ::Nil\n # *message* to its recipients - To, Cc and Bcc - from its From. The\n # message must have a From and a Date, as `Mail::Message#to_s` asks\n # (`msg.date = Mail.date(...)`), and at least one recipient. A message\n # without them is the caller's mistake and panics: `SMTP: a message has\n # no From`, then `to_s`'s `mail: a message has no Date`, then `SMTP: a\n # message with no recipients`. Every other failure - the connection, the\n # server's replies, a size or extension the server refuses - is the\n # `SmtpError` returned.\n def send(message : Mail::Message) : SmtpError | ::Nil\n # *data* as DATA sends it (section 4.5.2): line breaks CRLF, a dot\n # doubled at a line's start, a CRLF at the end if it had none, and the\n # lone dot after.\n def self.stuffed(data : String) : String\n # Whether the session is under TLS.\n def tls? : Bool\n def self.unsafe?(text : String) : Bool\nend\n\n# What a session answered that it should not have, or could not do, as a\n# value. `code` is the reply's code, 0 when there was no reply to have\n# one; `reply` the reply's text, its lines joined with newlines.\npub struct SmtpError\n def cancelled? : Bool\n def code : Int32\n def initialize(reason : String, code : Int32 = 0, reply : String = \"\", timed_out : Bool = false, cancelled : Bool = false)\n def reason : String\n def reply : String\n # Whether a read or a write waited `timeout_ms` and nothing came.\n def timed_out? : Bool\n def to_s : String\nend\n\n# One reply: its code and its lines' text, the code and separator taken\n# off each.\npub struct SmtpReply\n def code : Int32\n def initialize(code : Int32, lines : Array(String))\n def lines : Array(String)\n def text : String\nend\n\n# How the connection is kept from the wire.\npub enum SmtpSecurity : Int32\n StartTls = 0\n Tls = 1\n Cleartext = 2\nend\n\nimpl Error for SmtpError\n def message : String\nend\n" }, { "name": "std/socket", "path": "src/std/socket.iyi", - "sourceSha256": "eff1d8cd1e03a482ece90494c957df9f6d962808444e75f56a8b4843b2d5c894", - "surface": "module std/socket\n\n# What a resolver answers and is built from, under the names a program\n# that connects by name already imports from here.\npub alias DnsError = Std::Dns::DnsError\n\n# An encrypted way to a name server, asked by a resolver `Resolver#encrypted`\n# made instead of its plaintext servers: std/secure_dns's `DnsOverTls`\n# (RFC 7858) and `DnsOverHttps` (RFC 8484). The trait is here and they are\n# there because they speak std/tls and std/http, which import this module,\n# and R-1 refuses the cycle - as std/http's `CookieStore` is std/cookie's\n# to implement.\npub trait DnsUpstream\n # Its connections closed; the next exchange opens new ones.\n abstract def close : Nil\n # The answer to each of *queries*, in their order, each one whole\n # message as it came; or why there are none. *timeout_ms* bounds each\n # wait on the server. An answer is matched to its query by the\n # transport - by ID over one connection, by request over HTTP - and the\n # resolver then reads it as it reads a datagram.\n abstract def exchange(queries : Array(Bytes), timeout_ms : Int32) : Array(Bytes) | DnsError | Cancelled\n # How a message names it: `dns.test (127.0.0.1:853) over TLS`.\n abstract def label : String\n # Whether its queries carry the ID 0: DNS over HTTPS's, which RFC 8484\n # 4.1 asks for so that an HTTP cache sees one request for one question.\n # The answer must carry the ID the query did all the same.\n abstract def zero_id? : Bool\nend\n\n# Either family's address: what a resolver answers for a name.\npub alias IPAddress = IPv4Address | IPv6Address\n\npub struct IPv4Address\n def b0 : UInt8\n def b1 : UInt8\n def b2 : UInt8\n def b3 : UInt8\n def initialize(b0 : UInt8, b1 : UInt8, b2 : UInt8, b3 : UInt8)\n def to_s : String\nend\n\n# Sixteen bytes in network order, held as two words: `high` is bytes 0 to\n# 7. Written the RFC 5952 way - lowercase, no leading zeros, the longest\n# run of two or more zero groups as `::`, the first of two equal runs -\n# and an IPv4-mapped address with its dotted tail, `::ffff:192.0.2.1`.\npub struct IPv6Address\n # Byte *index*, 0 to 15.\n def [](index : Int32) : UInt8\n # The 16-bit group *index*, 0 to 7. Not `group`: that name is the task\n # group's in iyi (SPEC.md III.4.9).\n def hextet(index : Int32) : Int32\n def high : UInt64\n def initialize(high : UInt64, low : UInt64)\n def low : UInt64\n # The sixteen bytes at *bytes*, network order.\n def self.new(bytes : Pointer(UInt8)) : IPv6Address\n def to_s : String\nend\n\npub class IyiSocket\n # Room for any family's `sockaddr`, zeroed, so a unix path the kernel\n # did not end with a NUL still ends.\n def self.__address_room : Pointer(UInt8)\n # A `sockaddr` the kernel filled, as text: dotted IPv4, IPv6 written as\n # RFC 5952 has it, or a unix socket's path.\n def self.__address_text(addr : Pointer(UInt8), family : Int32) : String\n # What every connected and accepted socket starts with: TCP_NODELAY, on\n # TCP; a unix socket has no Nagle.\n def self.__send_at_once(fd : Int32, family : Int32) : Nil\n def self.__sys3(number : Int64, a : Int64, b : Int64, c : Int64) : Int64\n def self.__sys5(number : Int64, a : Int64, b : Int64, c : Int64, d : Int64, e : Int64) : Int64\n def self.__sys6(number : Int64, a : Int64, b : Int64, c : Int64, d : Int64, e : Int64, f : Int64) : Int64\n def self.__sys_accept(fd : Int32, addr : Pointer(UInt8), addrlen : Pointer(UInt32), family : Int32) : Int32\n def self.__sys_addr_size(family : Int32) : Int64\n def self.__sys_bind(fd : Int32, addr : Pointer(UInt8), family : Int32) : Int32\n # Every close of a socket in this file goes through here. `__iyi_close` is\n # the descriptor close, and on Windows a socket is not a descriptor:\n # `CloseHandle` on a SOCKET releases the kernel handle and leaves Winsock's\n # half of it standing, and the graceful close that makes a peer's read\n # answer EOF rather than a reset is `closesocket`'s.\n def self.__sys_close(fd : Int32) : Nil\n # A raw syscall's negative errno, as the number.\n def self.__sys_code(result : Int64) : Int32\n # *ms* is `connect_to`'s wait's: the call answers EINPROGRESS at once.\n def self.__sys_connect(fd : Int32, addr : Pointer(UInt8), family : Int32, ms : Int32) : Int32\n def self.__sys_connects_blocking?(family : Int32) : ::Bool\n def self.__sys_dgram_connect(fd : Int32, addr : Pointer(UInt8), family : Int32) : Int32\n def self.__sys_dgram_send(fd : Int32, buf : Pointer(UInt8), len : UInt64) : Int64\n # The resolver's datagram socket: see the darwin arm. The raw call\n # answers the negative errno, which `__sys_code` reads.\n def self.__sys_dgram_socket(family : Int32) : Int32\n def self.__sys_dgram_wait(fd : Int32, ms : Int32) : ::Bool | Cancelled\n def self.__sys_getpeername(fd : Int32, addr : Pointer(UInt8), addrlen : Pointer(UInt32)) : Int32\n def self.__sys_getsockname(fd : Int32, addr : Pointer(UInt8), addrlen : Pointer(UInt32)) : Int32\n def self.__sys_in_progress?(result : Int32) : ::Bool\n def self.__sys_listen(fd : Int32, backlog : Int32) : Int32\n def self.__sys_no_sigpipe(fd : Int32) : Nil\n def self.__sys_nodelay?(fd : Int32) : ::Bool\n # `ppoll` on one descriptor for *ms* milliseconds - a timespec, or none\n # for -1 - and no signal mask. 0 when the time ran out.\n def self.__sys_poll(fd : Int32, events : Int32, ms : Int32) : Int32\n def self.__sys_recv(fd : Int32, buf : Pointer(UInt8), len : UInt64) : Int64\n # MSG_NOSIGNAL (0x4000): a write to a connection the peer closed answers\n # EPIPE instead of killing the process with SIGPIPE.\n def self.__sys_send(fd : Int32, buf : Pointer(UInt8), len : UInt64, ms : Int32) : Int64\n # TCP_NODELAY (1) at IPPROTO_TCP (6): Nagle's algorithm off when *on*.\n def self.__sys_set_nodelay(fd : Int32, on : ::Bool) : Int32\n # F_GETFL (3), then F_SETFL (4) with O_NONBLOCK (0x800) added.\n def self.__sys_set_nonblock(fd : Int32) : Int32\n def self.__sys_set_reuseaddr(fd : Int32) : Int32\n # IPV6_V6ONLY (26) at IPPROTO_IPV6 (41).\n def self.__sys_set_v6only(fd : Int32) : Int32\n def self.__sys_shutdown_write(fd : Int32) : Int64\n # SO_ERROR (4) at SOL_SOCKET (1): what a connect that was in progress\n # ended with, 0 when it went through.\n def self.__sys_sock_error(fd : Int32) : Int32\n def self.__sys_socket(family : Int32) : Int32\n # A raw syscall answers the negative errno: EAGAIN is 11, EINPROGRESS 115.\n def self.__sys_would_block?(result : Int64) : ::Bool\n def accept : IyiSocket | Cancelled | SocketError\n # The order `connect` tries a name's addresses in (RFC 8305 section 4):\n # one family's first address, then the other's, alternating, each family\n # in the resolver's order, and the longer family's rest at the end.\n # IPv6 goes first when this machine has a route to the first IPv6\n # address - RFC 6724's first rule, asked the way glibc's getaddrinfo\n # asks it, by connecting a UDP socket, which sends nothing - and IPv4\n # first when it has none, so a machine without IPv6 does not start every\n # connect with an attempt that cannot leave it. A name of one family is\n # left in the resolver's order, and asks nothing.\n def self.attempt_order(addresses : Array(IPAddress)) : Array(IPAddress)\n def self.await_readable(fd : Int32, ms : Int32 = -1) : ::Bool | Cancelled\n def self.await_writable(fd : Int32, ms : Int32 = -1) : ::Bool | Cancelled\n # A timeout as the waits take it: milliseconds, -1 for none.\n def self.bound(ms : Int32 | ::Nil) : Int32\n protected def bound_path(path : String) : Nil\n # *path* with the NUL a system call reads to.\n def self.c_path(path : String) : Pointer(UInt8)\n def close : Nil\n # The sending half ended, the receiving half kept: the peer reads the\n # end of the stream after what was written, and this end goes on reading\n # what the peer still sends until its own end. `shutdown(SHUT_WR)` on\n # POSIX, `shutdown(SD_SEND)` on Windows. A socket that is not connected\n # answers the platform's refusal as a `SocketError`; `close` is still\n # what gives the socket back.\n def close_write : SocketError | ::Nil\n def closed? : ::Bool\n # A host name is resolved by the machine's resolver (`Resolver.system`);\n # a name that does not resolve is a `SocketError` whose `unresolved?` is\n # true and whose sentence names the host. It panicked, \"cannot resolve\n # address: \\\"example.com\\\"\", for every name but `localhost`, so\n # `HTTP.get(\"http://example.com/\")` could not run.\n #\n # A name with more than one address is raced, Happy Eyeballs v2 (RFC\n # 8305): the first address is tried, the next `ATTEMPT_DELAY_MS` later\n # without waiting for the first to fail, or at once when an attempt\n # fails, and so on down the list (`attempt_order`'s: IPv6 first when\n # this machine has a route to it, the families alternating). The first\n # handshake to finish wins, and every other attempt is cancelled and its\n # socket closed before `connect` answers. The addresses were tried one\n # after another, IPv4 first, each with the whole timeout: a name whose\n # first address dropped SYNs cost all of *timeout_ms* - or the kernel's\n # two minutes of retries, unbounded - before the second was tried. Now\n # it costs the delay: 250 ms to a name whose first address is a full\n # listen queue and whose second listens (`bench/std_dns_exercise.sh`,\n # under a 5,000 ms bound).\n #\n # `localhost` is raced the same way, 127.0.0.1 and then ::1, without\n # asking the routing table: a listener here binds `localhost` at\n # 127.0.0.1, so it goes first, and Windows, which names ::1 first, has\n # dev servers that bind ::1 alone. This replaces the rule that tried\n # ::1 only after 127.0.0.1 refused: Windows takes 2 s to refuse a\n # connect, so a ::1-only listener was reached in 2017 ms and a port\n # nobody holds refused in 4059 ms, where now ::1 starts 250 ms after\n # 127.0.0.1 whatever 127.0.0.1 is still doing.\n #\n # *timeout_ms* bounds the whole connect, every attempt together (-1: as\n # long as the kernel waits, which on Linux is about two minutes of SYN\n # retries for a host that drops them). It bounded each address's\n # handshake, which a race has no use for: an attempt started 500 ms in\n # would have run 500 ms past the caller's bound. A connect that runs out\n # is a `SocketError` whose `timed_out?` is true. The name's lookup is\n # outside it, held by the resolver's own `timeout_ms` and `attempts`.\n #\n # A name none of whose addresses connects is one `SocketError` that\n # names each address in the order tried and how its attempt ended,\n # `tried`: `cannot connect to example.com:443: [2001:db8::1]:443 timed\n # out; 192.0.2.1:443 connection refused`. It answered the first\n # address's failure alone. Its `code` is the first attempt's, or a\n # timeout's when the time ran out first. A name with one address is\n # connected to as an address is, and fails with that address's error.\n def self.connect(host : String, port : Int32, timeout_ms : Int32 = -1) : IyiSocket | Cancelled | SocketError\n # The same, with a host name resolved by *resolver*: name servers, a\n # hosts file and search list of the program's choosing, rather than the\n # machine's. An address and `localhost` are what they are either way.\n def self.connect(host : String, port : Int32, resolver : Resolver, timeout_ms : Int32 = -1) : IyiSocket | Cancelled | SocketError\n # A connect to *addr*, waiting at most *ms* milliseconds for the\n # handshake (-1: as long as the kernel does). The resolver's TCP query\n # and `connect` to an address are its callers with a bound; a race's\n # attempts wait as the kernel does, and the race keeps the bound. The\n # bound reaches `__sys_connect` as well as the wait after it, because\n # on Windows that call is the wait: it parks in `ConnectEx` until the\n # handshake is over.\n def self.connect_to(addr : Pointer(UInt8), family : Int32, peer : String, ms : Int32 = -1) : IyiSocket | Cancelled | SocketError\n # The stream socket listening at *path*.\n def self.connect_unix(path : String) : IyiSocket | Cancelled | SocketError\n # *host* and *port* as a message names them: an IPv6 literal in its\n # brackets, as a URL writes it. Bare, `::1:57456` was an address with\n # no port, and `ff02::1:80` another address altogether.\n def self.endpoint(host : String, port : Int32) : String\n # The address family, the platform's number for it: an accepted\n # connection is its listener's, and on Windows the socket `AcceptEx`\n # fills in has to be made in it. With `fd`, what makes the socket again\n # on another thread (std/http's `Server.serve`, on its pool).\n def family : Int32\n def fd : Int32\n def initialize(fd : Int32, family : Int32 = AF_INET)\n def self.listen(host : String, port : Int32, backlog : Int32 = 128) : IyiSocket\n def self.listen(port : Int32, backlog : Int32 = 128) : IyiSocket\n # A stream socket at *path*. The path must not exist: a socket file an\n # earlier run left behind is refused rather than taken over, since\n # which program owns it is not something a bind can know. `close`\n # removes the file.\n def self.listen_unix(path : String, backlog : Int32 = 128) : IyiSocket\n def local_address : String\n # The port sits at the same two bytes of a `sockaddr_in` and a\n # `sockaddr_in6`.\n def local_port : Int32\n # `localhost` in any case, and with the one trailing dot of a name\n # written fully qualified: a host name's case is not part of it (RFC\n # 4343), and getaddrinfo, Python and curl all answer 127.0.0.1 for\n # `LOCALHOST` and `localhost.`. Matched exactly, these were \"cannot\n # resolve address: \\\"LOCALHOST\\\"\", and so were `Localhost` and\n # `localhost.`.\n def self.localhost?(host : String) : ::Bool\n # What `localhost` stands for, in the order `connect` tries it.\n def self.loopback : Array(IPAddress)\n def self.make_sockaddr_in(ip : IPv4Address, port : Int32) : Pointer(UInt8)\n # A `sockaddr_in6`: family, port, a zero flow label, the address, a zero\n # scope. The family's number and where it sits are the platform's.\n def self.make_sockaddr_in6(ip : IPv6Address, port : Int32) : Pointer(UInt8)\n # A `sockaddr_un`: the family, then the path and its NUL. The room for\n # the path is the platform's - 104 bytes on darwin, 108 elsewhere - and a\n # longer one is refused rather than cut, since a cut path is another\n # path.\n def self.make_sockaddr_un(path : String) : Pointer(UInt8)\n # Whether a small write goes at once (TCP_NODELAY) rather than waiting\n # for an earlier one's acknowledgement, as Nagle's algorithm (RFC 896)\n # has it wait. A connected or accepted TCP socket here sends at once, as\n # Go's net package sets its own; `nodelay = false` turns Nagle back on\n # for a program that writes in pieces it would rather have joined. With\n # Nagle on, a write behind an unacknowledged one waited out the peer's\n # delayed ACK, 40 ms on Linux: measured on loopback, --release, medians\n # of 200, a GET on a new https:// connection took 82.89 ms between\n # std/http's ends and 42.00 to OpenSSL's s_server, and takes 1.07 and\n # 1.10 now; wrk against a server answering a streamed body went from\n # 1,211 to 10,702 requests a second. A unix socket has no Nagle to turn\n # off: it answers false and is left alone.\n def nodelay=(on : ::Bool) : ::Bool\n # Whether TCP_NODELAY is on, as the socket's option reads.\n def nodelay? : ::Bool\n # Dotted-decimal IPv4 only: four octets of 0 to 255, no leading zero\n # (`\"010.1.1.1\"` is refused; glibc's `inet_aton` reads `010` as octal 8,\n # CVE-2021-29921, so it must not quietly be 10 here), plus `localhost`.\n # The refusal inspects the host so a stray space shows.\n def self.parse_ip(host : String) : IPv4Address\n # Eight groups of up to four hex digits, `::` once for a run of zero\n # groups, and a dotted IPv4 tail for the last two (`::ffff:1.2.3.4`). A\n # zone (`fe80::1%eth0`) is refused by name: it names an interface, and\n # this parser has no way to ask the platform which.\n def self.parse_ipv6(host : String) : IPv6Address\n def read(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n def read(max_bytes : Int32 = 4096) : String | Cancelled | SocketError\n # The dotted-decimal reading alone, with the refusal's sentence as the\n # answer instead of a panic: the resolver reads the addresses a hosts\n # file and a resolv.conf write, and a line it cannot read is skipped\n # there, not the end of the program.\n def self.read_ipv4(host : String) : IPv4Address | String\n # The reading alone, the refusal's sentence as the answer: see\n # `read_ipv4`.\n def self.read_ipv6(host : String) : IPv6Address | String\n # The bounds on a read's and a write's wait, in milliseconds; nil waits\n # as long as it takes.\n def read_timeout_ms : Int32 | ::Nil\n def read_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\n def recv(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n def recv(max_bytes : Int32 = 4096) : String | Cancelled | SocketError\n # The other end's address and port, as `local_address` and\n # `local_port` give this end's: what a server logs a client by. A unix\n # socket's peer has a path at most, often none (\"\"), and no port. Nil\n # when the other end is gone - the connection was reset or never made -\n # which is the peer's doing and no fault of the program's.\n def remote_address : String | ::Nil\n def remote_port : Int32 | ::Nil\n # Every address *host* stands for, by the machine's resolver, in the\n # resolver's order (see `Resolver#resolve`); `connect` tries them in\n # `attempt_order`'s.\n def self.resolve(host : String) : Array(IPAddress) | DnsError | Cancelled\n def send(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n def send(contents : String) : Int32 | Cancelled | SocketError\n # A host and port as the address a call takes, and its family: an IPv6\n # literal bare or in brackets, anything else IPv4.\n def self.sockaddr(host : String, port : Int32) : Tuple(Pointer(UInt8), Int32)\n # This socket as a stream (`SocketIO`), the same one every time, so\n # what one read took ahead the next reads. It was a stream over the\n # descriptor that read a socket with nothing yet to read as failed.\n def to_io : Std::Socket::SocketIO\n def to_unsafe : Int32\n # Whether this is a unix socket, which has a path where TCP has a port:\n # `remote_port` and `local_port` refuse it. std/tls asks before it keys\n # a session's tickets by the port they came from.\n def unix? : ::Bool\n def write(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n def write(contents : String) : Int32 | Cancelled | SocketError\n def write_timeout_ms : Int32 | ::Nil\n def write_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\nend\n\npub alias ResolvConf = Std::Dns::ResolvConf\n\n# A stub resolver: a host name's addresses, from a hosts file and then\n# from name servers, as the C library's resolver finds them - but iyi's,\n# so asking costs no library (see the header of this file).\n#\n# `Resolver.system` is the machine's: `/etc/resolv.conf` and `/etc/hosts`\n# on Linux and darwin, the registry and\n# `%SystemRoot%\\System32\\drivers\\etc\\hosts` on Windows (see\n# `__sys_dns_config` for what the registry can and cannot say). A\n# resolver built with `new` or `from_config` asks exactly what it was\n# given, which is how a test asks a server it runs itself.\n#\n# `resolve` answers every address for a name, IPv4 first and then IPv6,\n# each family in the order the hosts file or the server gave it;\n# `connect` races them in `IyiSocket.attempt_order`'s order:\n#\n# 1. An address, or `localhost` (127.0.0.1 and ::1: RFC 6761 6.3 keeps it\n# off the network), is itself. Anything that is not a host name is\n# `invalid_name?`.\n# 2. The hosts file: every line naming the host, in any case. A name\n# found there is not asked about.\n# 3. The names the search list and `ndots` make of it (`DnsName.candidates`),\n# in order. Each is asked for A and then AAAA records; NXDOMAIN or no\n# records moves on to the next, and any other failure ends the lookup\n# with that failure - a server that timed out on `db.corp.example` must\n# not turn the answer into `db.example`'s (musl's rule; glibc moves on).\n#\n# A and AAAA are asked one after the other, and `connect` starts its race\n# once both have answered. RFC 8305 section 3 asks both at once and\n# starts connecting on the A answer when AAAA is 50 ms behind it; that\n# makes a lookup answer in pieces, and saves the AAAA query's round trip\n# at most - 5 to 65 ms uncached for five names through systemd-resolved\n# on the machine this was written on, 0 to 1 ms cached (dig's query\n# times) - where the race saves what an address that drops SYNs costs,\n# the whole timeout. Asked in turn, an NXDOMAIN for A spares the AAAA\n# query, as it does for most of a search list's candidates.\n#\n# A query is one UDP datagram from a fresh socket (so a fresh source\n# port), with an ID from `Random::Secure`, to each server in turn, each\n# waited for `timeout_ms`, and the round repeated `attempts` times. A\n# datagram that does not carry the ID and the question is ignored and the\n# wait goes on; one from any other address never arrives, because the\n# socket is connected to the server. An answer with TC set is asked again\n# over TCP. SERVFAIL, REFUSED and a server that does not answer move on to\n# the next server; NXDOMAIN is the answer. An answer that cannot be read\n# is ignored like a forgery, and is the failure reported if nothing\n# better arrives before the timeout.\n#\n# Encrypted:\n# `encrypted(upstreams)` is the same resolver - hosts file, search list,\n# `ndots`, timeout and attempts - asking `DnsUpstream`s instead of its\n# plaintext servers: DNS over TLS or over HTTPS, std/secure_dns's\n# `DnsOverTls` and `DnsOverHttps`. Each upstream is asked in turn, the\n# round repeated `attempts` times, as the servers are; A and AAAA go in\n# one exchange. An answer over one is held to more than a datagram is:\n# the ID and the question as ever, and no TC and no byte past the message,\n# since a stream has room for any answer and its frame is the message.\n# The plaintext servers are not asked at all, not even when every\n# upstream is down, unless the caller passes *plaintext_fallback*: RFC\n# 8310 5's strict profile, where a lookup that cannot be private fails,\n# is the default, because falling back hands the very question the\n# program meant to keep private to anyone on the path, and a program\n# that would rather have an answer says so. With *plaintext_fallback*\n# (the opportunistic profile) the plaintext servers are asked only when\n# no upstream could be: one that answered, SERVFAIL included, has spoken.\n# One task at a time per resolver with upstreams: an upstream holds its\n# connection, as an `HTTPClient` does.\npub class Resolver\n protected def ask_upstreams_instead(upstreams : Array(DnsUpstream), plaintext_fallback : Bool) : Nil\n def attempts : Int32\n # *records* by ascending priority, those of one priority shuffled.\n def self.by_priority(records : Array(SvcbRecord)) : Array(SvcbRecord)\n # Every upstream's connections closed.\n def close : Nil\n # This resolver asking *upstreams* instead of its plaintext servers,\n # which are asked only with *plaintext_fallback*, and then only when no\n # upstream could be (see the class).\n def encrypted(upstreams : Array(DnsUpstream), plaintext_fallback : Bool = false) : Resolver\n def encrypted(upstream : DnsUpstream, plaintext_fallback : Bool = false) : Resolver\n # The machine's resolver - its hosts file and search list - asking\n # *upstreams*; its name servers only with *plaintext_fallback*.\n def self.encrypted(upstreams : Array(DnsUpstream), plaintext_fallback : Bool = false) : Resolver\n def self.encrypted(upstream : DnsUpstream, plaintext_fallback : Bool = false) : Resolver\n # A resolver from a parsed resolv.conf and a hosts file's text. A\n # `nameserver` line this resolver cannot use - an IPv6 address with a\n # zone, a typo - is skipped as glibc skips one; with none left, the\n # servers a machine without a configuration is assumed to run.\n def self.from_config(config : ResolvConf, hosts : String = \"\") : Resolver\n # A resolver from a resolv.conf and a hosts file at these paths. A file\n # that is not there reads as empty, as it does to the C library.\n def self.from_files(resolv_conf : String, hosts : String) : Resolver\n # The hosts file's text, read before any server is asked.\n def hosts : String\n # The HTTPS records (RFC 9460 section 9) of the origin `https://host:port`,\n # by SVCB resolution (section 3): the question is *host* for port 443\n # and `_port._https.host` for any other (section 9.1); an AliasMode\n # answer is followed to its target's HTTPS records, at most\n # `SvcbAnswer::MAX_ALIASES` of them, and CNAMEs on the way as `txt`\n # follows them; and the ServiceMode records found are answered by\n # priority, those of one priority in a random order (section 2.4.1).\n # The name is asked as written, as for `txt`, over the upstreams when\n # the resolver has them.\n #\n # Of an AliasMode set one record is followed, at random where there are\n # several (section 2.4.2), and any ServiceMode record beside it is\n # passed over (section 2.4.1). A target that has no HTTPS records ends\n # resolution there, with none: the answer's `name` is the target, which\n # section 3 still has a client connect to. An answer with a record\n # `SvcbRecord.parse` refuses is the whole set refused, `malformed?`, as\n # section 2.2 has it; so is a chain longer than the bound. NXDOMAIN and\n # NODATA for the origin's own name are `not_found?` and `no_address?`,\n # as they are for an address.\n def https_records(host : String, port : Int32 = 443) : SvcbAnswer | DnsError | Cancelled\n # *nameservers* are addresses, each with an optional port:\n # `\"192.0.2.1\"`, `\"192.0.2.1:5353\"`, `\"2001:db8::1\"`,\n # `\"[2001:db8::1]:5353\"`. One that is not is refused here, by name: it is\n # the program's own mistake. None at all is a resolver that reads only\n # *hosts*.\n def initialize(nameservers : Array(String), search : Array(String) = [] of String, ndots : Int32 = 1, timeout_ms : Int32 = 5000, attempts : Int32 = 2, hosts : String = \"\")\n # What NODATA for *type* says the name lacks: its records, by name, for\n # the types asked other than as addresses.\n def self.missing(type : Int32) : String\n def nameservers : Array(String)\n def ndots : Int32\n # The monotonic clock in milliseconds: the runtime's, which its sleep\n # queue keeps deadlines by. A target without the runtime has no socket\n # to wait on either (`__sys_dgram_socket` refuses there), so nothing\n # there reads a time.\n def self.now_ms : Int64\n # The answer to one question over plaintext: each server in turn, the\n # round repeated `attempts` times, until one answers NOERROR or\n # NXDOMAIN. The failure answered otherwise is the last server's.\n protected def plaintext(name : String, type : Int32) : Tuple(DnsMessage, String) | DnsError | Cancelled\n # Whether `nameservers` are asked when no upstream could be (RFC 8310's\n # opportunistic profile); false, the strict one, by default.\n def plaintext_fallback : Bool\n def self.rcode_name(rcode : Int32) : String\n # What an answer's RCODE makes of it: the message for NOERROR, and the\n # failure it says otherwise.\n def self.read_rcode(message : DnsMessage, name : String, label : String) : Tuple(DnsMessage, String) | DnsError\n # An answer that came over a stream, read: see `ask_upstream`.\n def self.read_streamed(bytes : Bytes, id : UInt16, name : String, type : Int32, label : String) : Tuple(DnsMessage, String) | DnsError\n # Every address *host* stands for: see the class's comment for the\n # order things are asked in.\n def resolve(host : String) : Array(IPAddress) | DnsError | Cancelled\n def search : Array(String)\n # A name server written as an address with an optional port, or the\n # sentence saying why it is not one.\n def self.server(text : String) : NameServer | String\n # The machine's resolver, read afresh each time: a configuration the\n # network manager rewrote is the one the next lookup uses.\n def self.system : Resolver\n def timeout_ms : Int32\n # The TXT records of *name*, each one's character-strings joined\n # (`DnsRecord.character_strings`), in the order the answer gave them,\n # CNAMEs followed as `resolve` follows them. The name is asked as\n # written - no hosts file, no search list - since a TXT name\n # (`_acme-challenge.example.com`, a domain's SPF) is written in full.\n def txt(name : String) : Array(String) | DnsError | Cancelled\n # The encrypted upstreams asked instead of `nameservers`; none for a\n # plaintext resolver.\n def upstreams : Array(DnsUpstream)\nend\n\n# A connection's own failure: which operation, and the platform's error\n# number for why - or, for a host name that could not be turned into an\n# address, the resolver's `DnsError`, and no number (`code` 0); or, for a\n# protocol layered on the connection (TLS), that protocol's own sentence,\n# and no number either; or, for a name whose addresses were all tried and\n# none connected, every one of them with how its attempt ended.\npub struct SocketError\n def code : Int32\n # The peer is gone: it reset the connection, or closed it and the write\n # found the pipe broken, or the connection was aborted.\n def disconnected? : ::Bool\n # Why the host's name did not resolve; nil for every other failure.\n def dns_error : DnsError | ::Nil\n # A connect to a name none of whose addresses took the connection:\n # *code* is what `refused?` and `timed_out?` read, and *tried* the\n # sentence naming every address.\n def self.every_address(operation : String, code : Int32, tried : String) : SocketError\n def initialize(operation : String, code : Int32, dns_error : DnsError | ::Nil, layer_reason : String | ::Nil, tried : String | ::Nil)\n def initialize(operation : String, code : Int32, dns_error : DnsError | ::Nil, layer_reason : String | ::Nil)\n def initialize(operation : String, code : Int32, dns_error : DnsError | ::Nil)\n def initialize(operation : String, code : Int32)\n # What the protocol over the connection said went wrong; nil otherwise.\n def layer_reason : String | ::Nil\n # A failure of the protocol over the connection, in its words: `cannot\n # read from example.com: tls: a record that does not decrypt`.\n def self.layered(operation : String, reason : String) : SocketError\n def operation : String\n def reason : String\n def refused? : ::Bool\n # A wait for the peer that ran past its socket's timeout, carrying the\n # platform's own number for it so `reason` and `timed_out?` read it as\n # they read the kernel's.\n def self.timed_out(operation : String) : SocketError\n # The peer did not answer in time: a connect the network timed out, or a\n # read or write past its socket's timeout.\n def timed_out? : ::Bool\n # For a connect to a name that stood for more than one address: each\n # address in the order it was tried and how its attempt ended -\n # `[2001:db8::1]:443 timed out; 192.0.2.1:443 connection refused` - which\n # `reason` answers. Nil for every other failure.\n def tried : String | ::Nil\n def unreachable? : ::Bool\n # The host was a name, and no address came of it: `dns_error` says why\n # (`not_found?` for NXDOMAIN, `server_failure?` for SERVFAIL, ...).\n def unresolved? : ::Bool\nend\n\n# A socket as a stream, for what is written against streams: `read_line`,\n# `puts`, `IyiIO.copy` between it and a file or a pipe. Its reads and\n# writes are the socket's own, which park the task and keep its\n# timeouts. A stream has no error to answer, so a read the socket refuses\n# ends the stream, `read_failed?` true, and a write it refuses panics,\n# naming the reason; a program that answers those reads the socket.\n# Writes are buffered: `flush`, or `IyiIO.copy`, which flushes, sends\n# them. Closing it closes the socket.\npub class SocketIO < ::IyiIO\n def initialize(socket : IyiSocket, buffer_size : Int32 = 4096)\n def socket : IyiSocket\nend\n\nimpl Error for SocketError\n def message : String\nend\n" + "sourceSha256": "e1aed6d7be317e86498164135754a78ca1535000053b26562db8b42d60780b0f", + "surface": "module std/socket\n\n# What a resolver answers and is built from, under the names a program\n# that connects by name already imports from here.\npub alias DnsError = Std::Dns::DnsError\n\n# An encrypted way to a name server, asked by a resolver `Resolver#encrypted`\n# made instead of its plaintext servers: std/secure_dns's `DnsOverTls`\n# (RFC 7858) and `DnsOverHttps` (RFC 8484). The trait is here and they are\n# there because they speak std/tls and std/http, which import this module,\n# and R-1 refuses the cycle - as std/http's `CookieStore` is std/cookie's\n# to implement.\npub trait DnsUpstream\n # Its connections closed; the next exchange opens new ones.\n abstract def close : Nil\n # The answer to each of *queries*, in their order, each one whole\n # message as it came; or why there are none. *timeout_ms* bounds each\n # wait on the server. An answer is matched to its query by the\n # transport - by ID over one connection, by request over HTTP - and the\n # resolver then reads it as it reads a datagram.\n abstract def exchange(queries : Array(Bytes), timeout_ms : Int32) : Array(Bytes) | DnsError | Cancelled\n # How a message names it: `dns.test (127.0.0.1:853) over TLS`.\n abstract def label : String\n # Whether its queries carry the ID 0: DNS over HTTPS's, which RFC 8484\n # 4.1 asks for so that an HTTP cache sees one request for one question.\n # The answer must carry the ID the query did all the same.\n abstract def zero_id? : Bool\nend\n\n# Either family's address: what a resolver answers for a name.\npub alias IPAddress = IPv4Address | IPv6Address\n\npub struct IPv4Address\n def b0 : UInt8\n def b1 : UInt8\n def b2 : UInt8\n def b3 : UInt8\n def initialize(b0 : UInt8, b1 : UInt8, b2 : UInt8, b3 : UInt8)\n def to_s : String\nend\n\n# Sixteen bytes in network order, held as two words: `high` is bytes 0 to\n# 7. Written the RFC 5952 way - lowercase, no leading zeros, the longest\n# run of two or more zero groups as `::`, the first of two equal runs -\n# and an IPv4-mapped address with its dotted tail, `::ffff:192.0.2.1`.\npub struct IPv6Address\n # Byte *index*, 0 to 15.\n def [](index : Int32) : UInt8\n # The 16-bit group *index*, 0 to 7. Not `group`: that name is the task\n # group's in iyi (SPEC.md III.4.9).\n def hextet(index : Int32) : Int32\n def high : UInt64\n def initialize(high : UInt64, low : UInt64)\n def low : UInt64\n # The sixteen bytes at *bytes*, network order.\n def self.new(bytes : Pointer(UInt8)) : IPv6Address\n def to_s : String\nend\n\npub class IyiSocket\n # Room for any family's `sockaddr`, zeroed, so a unix path the kernel\n # did not end with a NUL still ends.\n def self.__address_room : Pointer(UInt8)\n # A `sockaddr` the kernel filled, as text: dotted IPv4, IPv6 written as\n # RFC 5952 has it, or a unix socket's path.\n def self.__address_text(addr : Pointer(UInt8), family : Int32) : String\n # What every connected and accepted socket starts with: TCP_NODELAY, on\n # TCP; a unix socket has no Nagle.\n def self.__send_at_once(fd : Int32, family : Int32) : Nil\n def self.__sys3(number : Int64, a : Int64, b : Int64, c : Int64) : Int64\n def self.__sys5(number : Int64, a : Int64, b : Int64, c : Int64, d : Int64, e : Int64) : Int64\n def self.__sys6(number : Int64, a : Int64, b : Int64, c : Int64, d : Int64, e : Int64, f : Int64) : Int64\n def self.__sys_accept(fd : Int32, addr : Pointer(UInt8), addrlen : Pointer(UInt32), family : Int32) : Int32\n def self.__sys_addr_size(family : Int32) : Int64\n def self.__sys_bind(fd : Int32, addr : Pointer(UInt8), family : Int32) : Int32\n # Every close of a socket in this file goes through here. `__iyi_close` is\n # the descriptor close, and on Windows a socket is not a descriptor:\n # `CloseHandle` on a SOCKET releases the kernel handle and leaves Winsock's\n # half of it standing, and the graceful close that makes a peer's read\n # answer EOF rather than a reset is `closesocket`'s.\n def self.__sys_close(fd : Int32) : Nil\n # A raw syscall's negative errno, as the number.\n def self.__sys_code(result : Int64) : Int32\n # *ms* is `connect_to`'s wait's: the call answers EINPROGRESS at once.\n def self.__sys_connect(fd : Int32, addr : Pointer(UInt8), family : Int32, ms : Int32) : Int32\n def self.__sys_connects_blocking?(family : Int32) : ::Bool\n def self.__sys_dgram_connect(fd : Int32, addr : Pointer(UInt8), family : Int32) : Int32\n def self.__sys_dgram_send(fd : Int32, buf : Pointer(UInt8), len : UInt64) : Int64\n # The resolver's datagram socket: see the darwin arm. The raw call\n # answers the negative errno, which `__sys_code` reads.\n def self.__sys_dgram_socket(family : Int32) : Int32\n def self.__sys_dgram_wait(fd : Int32, ms : Int32) : ::Bool | Cancelled\n def self.__sys_getpeername(fd : Int32, addr : Pointer(UInt8), addrlen : Pointer(UInt32)) : Int32\n def self.__sys_getsockname(fd : Int32, addr : Pointer(UInt8), addrlen : Pointer(UInt32)) : Int32\n def self.__sys_in_progress?(result : Int32) : ::Bool\n def self.__sys_listen(fd : Int32, backlog : Int32) : Int32\n def self.__sys_no_sigpipe(fd : Int32) : Nil\n def self.__sys_nodelay?(fd : Int32) : ::Bool\n # `ppoll` on one descriptor for *ms* milliseconds - a timespec, or none\n # for -1 - and no signal mask. 0 when the time ran out.\n def self.__sys_poll(fd : Int32, events : Int32, ms : Int32) : Int32\n def self.__sys_recv(fd : Int32, buf : Pointer(UInt8), len : UInt64) : Int64\n # MSG_NOSIGNAL (0x4000): a write to a connection the peer closed answers\n # EPIPE instead of killing the process with SIGPIPE.\n def self.__sys_send(fd : Int32, buf : Pointer(UInt8), len : UInt64, ms : Int32) : Int64\n # TCP_NODELAY (1) at IPPROTO_TCP (6): Nagle's algorithm off when *on*.\n def self.__sys_set_nodelay(fd : Int32, on : ::Bool) : Int32\n # F_GETFL (3), then F_SETFL (4) with O_NONBLOCK (0x800) added.\n def self.__sys_set_nonblock(fd : Int32) : Int32\n def self.__sys_set_reuseaddr(fd : Int32) : Int32\n # IPV6_V6ONLY (26) at IPPROTO_IPV6 (41).\n def self.__sys_set_v6only(fd : Int32) : Int32\n def self.__sys_shutdown_write(fd : Int32) : Int64\n # SO_ERROR (4) at SOL_SOCKET (1): what a connect that was in progress\n # ended with, 0 when it went through.\n def self.__sys_sock_error(fd : Int32) : Int32\n def self.__sys_socket(family : Int32) : Int32\n # A raw syscall answers the negative errno: EAGAIN is 11, EINPROGRESS 115.\n def self.__sys_would_block?(result : Int64) : ::Bool\n def accept : IyiSocket | Cancelled | SocketError\n # The order `connect` tries a name's addresses in (RFC 8305 section 4):\n # one family's first address, then the other's, alternating, each family\n # in the resolver's order, and the longer family's rest at the end.\n # IPv6 goes first when this machine has a route to the first IPv6\n # address - RFC 6724's first rule, asked the way glibc's getaddrinfo\n # asks it, by connecting a UDP socket, which sends nothing - and IPv4\n # first when it has none, so a machine without IPv6 does not start every\n # connect with an attempt that cannot leave it. A name of one family is\n # left in the resolver's order, and asks nothing.\n def self.attempt_order(addresses : Array(IPAddress)) : Array(IPAddress)\n def self.await_readable(fd : Int32, ms : Int32 = -1) : ::Bool | Cancelled\n def self.await_writable(fd : Int32, ms : Int32 = -1) : ::Bool | Cancelled\n # A timeout as the waits take it: milliseconds, -1 for none.\n def self.bound(ms : Int32 | ::Nil) : Int32\n protected def bound_path(path : String) : Nil\n # *path* with the NUL a system call reads to.\n def self.c_path(path : String) : Pointer(UInt8)\n def close : Nil\n # The sending half ended, the receiving half kept: the peer reads the\n # end of the stream after what was written, and this end goes on reading\n # what the peer still sends until its own end. `shutdown(SHUT_WR)` on\n # POSIX, `shutdown(SD_SEND)` on Windows. A socket that is not connected\n # answers the platform's refusal as a `SocketError`; `close` is still\n # what gives the socket back.\n def close_write : SocketError | ::Nil\n def closed? : ::Bool\n # A host name is resolved by the machine's resolver (`Resolver.system`);\n # a name that does not resolve is a `SocketError` whose `unresolved?` is\n # true and whose sentence names the host. It panicked, \"cannot resolve\n # address: \\\"example.com\\\"\", for every name but `localhost`, so\n # `HTTP.get(\"http://example.com/\")` could not run.\n #\n # A name with more than one address is raced, Happy Eyeballs v2 (RFC\n # 8305): the first address is tried, the next `ATTEMPT_DELAY_MS` later\n # without waiting for the first to fail, or at once when an attempt\n # fails, and so on down the list (`attempt_order`'s: IPv6 first when\n # this machine has a route to it, the families alternating). The first\n # handshake to finish wins, and every other attempt is cancelled and its\n # socket closed before `connect` answers. The addresses were tried one\n # after another, IPv4 first, each with the whole timeout: a name whose\n # first address dropped SYNs cost all of *timeout_ms* - or the kernel's\n # two minutes of retries, unbounded - before the second was tried. Now\n # it costs the delay: 250 ms to a name whose first address is a full\n # listen queue and whose second listens (`bench/std_dns_exercise.sh`,\n # under a 5,000 ms bound).\n #\n # `localhost` is raced the same way, 127.0.0.1 and then ::1, without\n # asking the routing table: a listener here binds `localhost` at\n # 127.0.0.1, so it goes first, and Windows, which names ::1 first, has\n # dev servers that bind ::1 alone. This replaces the rule that tried\n # ::1 only after 127.0.0.1 refused: Windows takes 2 s to refuse a\n # connect, so a ::1-only listener was reached in 2017 ms and a port\n # nobody holds refused in 4059 ms, where now ::1 starts 250 ms after\n # 127.0.0.1 whatever 127.0.0.1 is still doing.\n #\n # *timeout_ms* bounds the whole connect, every attempt together (-1: as\n # long as the kernel waits, which on Linux is about two minutes of SYN\n # retries for a host that drops them). It bounded each address's\n # handshake, which a race has no use for: an attempt started 500 ms in\n # would have run 500 ms past the caller's bound. A connect that runs out\n # is a `SocketError` whose `timed_out?` is true. The name's lookup is\n # outside it, held by the resolver's own `timeout_ms` and `attempts`.\n #\n # A name none of whose addresses connects is one `SocketError` that\n # names each address in the order tried and how its attempt ended,\n # `tried`: `cannot connect to example.com:443: [2001:db8::1]:443 timed\n # out; 192.0.2.1:443 connection refused`. It answered the first\n # address's failure alone. Its `code` is the first attempt's, or a\n # timeout's when the time ran out first. A name with one address is\n # connected to as an address is, and fails with that address's error.\n def self.connect(host : String, port : Int32, timeout_ms : Int32 = -1) : IyiSocket | Cancelled | SocketError\n # The same, with a host name resolved by *resolver*: name servers, a\n # hosts file and search list of the program's choosing, rather than the\n # machine's. An address and `localhost` are what they are either way.\n def self.connect(host : String, port : Int32, resolver : Resolver, timeout_ms : Int32 = -1) : IyiSocket | Cancelled | SocketError\n # A connect to *addr*, waiting at most *ms* milliseconds for the\n # handshake (-1: as long as the kernel does). The resolver's TCP query\n # and `connect` to an address are its callers with a bound; a race's\n # attempts wait as the kernel does, and the race keeps the bound. The\n # bound reaches `__sys_connect` as well as the wait after it, because\n # on Windows that call is the wait: it parks in `ConnectEx` until the\n # handshake is over.\n def self.connect_to(addr : Pointer(UInt8), family : Int32, peer : String, ms : Int32 = -1) : IyiSocket | Cancelled | SocketError\n # The stream socket listening at *path*.\n def self.connect_unix(path : String) : IyiSocket | Cancelled | SocketError\n # *host* and *port* as a message names them: an IPv6 literal in its\n # brackets, as a URL writes it. Bare, `::1:57456` was an address with\n # no port, and `ff02::1:80` another address altogether.\n def self.endpoint(host : String, port : Int32) : String\n # The address family, the platform's number for it: an accepted\n # connection is its listener's, and on Windows the socket `AcceptEx`\n # fills in has to be made in it. With `fd`, what makes the socket again\n # on another thread (std/http's `Server.serve`, on its pool).\n def family : Int32\n def fd : Int32\n def initialize(fd : Int32, family : Int32 = AF_INET)\n def self.listen(host : String, port : Int32, backlog : Int32 = 128) : IyiSocket\n def self.listen(port : Int32, backlog : Int32 = 128) : IyiSocket\n # A stream socket at *path*. The path must not exist: a socket file an\n # earlier run left behind is refused rather than taken over, since\n # which program owns it is not something a bind can know. `close`\n # removes the file.\n def self.listen_unix(path : String, backlog : Int32 = 128) : IyiSocket\n def local_address : String\n # The port sits at the same two bytes of a `sockaddr_in` and a\n # `sockaddr_in6`.\n def local_port : Int32\n # `localhost` in any case, and with the one trailing dot of a name\n # written fully qualified: a host name's case is not part of it (RFC\n # 4343), and getaddrinfo, Python and curl all answer 127.0.0.1 for\n # `LOCALHOST` and `localhost.`. Matched exactly, these were \"cannot\n # resolve address: \\\"LOCALHOST\\\"\", and so were `Localhost` and\n # `localhost.`.\n def self.localhost?(host : String) : ::Bool\n # What `localhost` stands for, in the order `connect` tries it.\n def self.loopback : Array(IPAddress)\n def self.make_sockaddr_in(ip : IPv4Address, port : Int32) : Pointer(UInt8)\n # A `sockaddr_in6`: family, port, a zero flow label, the address, a zero\n # scope. The family's number and where it sits are the platform's.\n def self.make_sockaddr_in6(ip : IPv6Address, port : Int32) : Pointer(UInt8)\n # A `sockaddr_un`: the family, then the path and its NUL. The room for\n # the path is the platform's - 104 bytes on darwin, 108 elsewhere - and a\n # longer one is refused rather than cut, since a cut path is another\n # path.\n def self.make_sockaddr_un(path : String) : Pointer(UInt8)\n # Whether a small write goes at once (TCP_NODELAY) rather than waiting\n # for an earlier one's acknowledgement, as Nagle's algorithm (RFC 896)\n # has it wait. A connected or accepted TCP socket here sends at once, as\n # Go's net package sets its own; `nodelay = false` turns Nagle back on\n # for a program that writes in pieces it would rather have joined. With\n # Nagle on, a write behind an unacknowledged one waited out the peer's\n # delayed ACK, 40 ms on Linux: measured on loopback, --release, medians\n # of 200, a GET on a new https:// connection took 82.89 ms between\n # std/http's ends and 42.00 to OpenSSL's s_server, and takes 1.07 and\n # 1.10 now; wrk against a server answering a streamed body went from\n # 1,211 to 10,702 requests a second. A unix socket has no Nagle to turn\n # off: it answers false and is left alone.\n def nodelay=(on : ::Bool) : ::Bool\n # Whether TCP_NODELAY is on, as the socket's option reads.\n def nodelay? : ::Bool\n # Dotted-decimal IPv4 only: four octets of 0 to 255, no leading zero\n # (`\"010.1.1.1\"` is refused; glibc's `inet_aton` reads `010` as octal 8,\n # CVE-2021-29921, so it must not quietly be 10 here), plus `localhost`.\n # The refusal inspects the host so a stray space shows.\n def self.parse_ip(host : String) : IPv4Address\n # Eight groups of up to four hex digits, `::` once for a run of zero\n # groups, and a dotted IPv4 tail for the last two (`::ffff:1.2.3.4`). A\n # zone (`fe80::1%eth0`) is refused by name: it names an interface, and\n # this parser has no way to ask the platform which.\n def self.parse_ipv6(host : String) : IPv6Address\n def read(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n def read(max_bytes : Int32 = 4096) : String | Cancelled | SocketError\n # Reads into *buffer*, as many bytes as one read gives and at most its\n # size: the count, or why not. The checked spelling of\n # `read(buffer.to_unsafe, buffer.size)` (SPEC.md III.13).\n def read(buffer : Bytes) : Int32 | Cancelled | SocketError\n # The dotted-decimal reading alone, with the refusal's sentence as the\n # answer instead of a panic: the resolver reads the addresses a hosts\n # file and a resolv.conf write, and a line it cannot read is skipped\n # there, not the end of the program.\n def self.read_ipv4(host : String) : IPv4Address | String\n # The reading alone, the refusal's sentence as the answer: see\n # `read_ipv4`.\n def self.read_ipv6(host : String) : IPv6Address | String\n # The bounds on a read's and a write's wait, in milliseconds; nil waits\n # as long as it takes.\n def read_timeout_ms : Int32 | ::Nil\n def read_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\n def recv(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n def recv(max_bytes : Int32 = 4096) : String | Cancelled | SocketError\n # The other end's address and port, as `local_address` and\n # `local_port` give this end's: what a server logs a client by. A unix\n # socket's peer has a path at most, often none (\"\"), and no port. Nil\n # when the other end is gone - the connection was reset or never made -\n # which is the peer's doing and no fault of the program's.\n def remote_address : String | ::Nil\n def remote_port : Int32 | ::Nil\n # Every address *host* stands for, by the machine's resolver, in the\n # resolver's order (see `Resolver#resolve`); `connect` tries them in\n # `attempt_order`'s.\n def self.resolve(host : String) : Array(IPAddress) | DnsError | Cancelled\n def send(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n def send(contents : String) : Int32 | Cancelled | SocketError\n # A host and port as the address a call takes, and its family: an IPv6\n # literal bare or in brackets, anything else IPv4.\n def self.sockaddr(host : String, port : Int32) : Tuple(Pointer(UInt8), Int32)\n # This socket as a stream (`SocketIO`), the same one every time, so\n # what one read took ahead the next reads. It was a stream over the\n # descriptor that read a socket with nothing yet to read as failed.\n def to_io : Std::Socket::SocketIO\n def to_unsafe : Int32\n # Whether this is a unix socket, which has a path where TCP has a port:\n # `remote_port` and `local_port` refuse it. std/tls asks before it keys\n # a session's tickets by the port they came from.\n def unix? : ::Bool\n def write(buffer : Pointer(UInt8), count : Int32) : Int32 | Cancelled | SocketError\n def write(contents : String) : Int32 | Cancelled | SocketError\n # Writes all of *bytes*: the count, or why not.\n def write(bytes : Bytes) : Int32 | Cancelled | SocketError\n def write_timeout_ms : Int32 | ::Nil\n def write_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\nend\n\npub alias ResolvConf = Std::Dns::ResolvConf\n\n# A stub resolver: a host name's addresses, from a hosts file and then\n# from name servers, as the C library's resolver finds them - but iyi's,\n# so asking costs no library (see the header of this file).\n#\n# `Resolver.system` is the machine's: `/etc/resolv.conf` and `/etc/hosts`\n# on Linux and darwin, the registry and\n# `%SystemRoot%\\System32\\drivers\\etc\\hosts` on Windows (see\n# `__sys_dns_config` for what the registry can and cannot say). A\n# resolver built with `new` or `from_config` asks exactly what it was\n# given, which is how a test asks a server it runs itself.\n#\n# `resolve` answers every address for a name, IPv4 first and then IPv6,\n# each family in the order the hosts file or the server gave it;\n# `connect` races them in `IyiSocket.attempt_order`'s order:\n#\n# 1. An address, or `localhost` (127.0.0.1 and ::1: RFC 6761 6.3 keeps it\n# off the network), is itself. Anything that is not a host name is\n# `invalid_name?`.\n# 2. The hosts file: every line naming the host, in any case. A name\n# found there is not asked about.\n# 3. The names the search list and `ndots` make of it (`DnsName.candidates`),\n# in order. Each is asked for A and then AAAA records; NXDOMAIN or no\n# records moves on to the next, and any other failure ends the lookup\n# with that failure - a server that timed out on `db.corp.example` must\n# not turn the answer into `db.example`'s (musl's rule; glibc moves on).\n#\n# A and AAAA are asked one after the other, and `connect` starts its race\n# once both have answered. RFC 8305 section 3 asks both at once and\n# starts connecting on the A answer when AAAA is 50 ms behind it; that\n# makes a lookup answer in pieces, and saves the AAAA query's round trip\n# at most - 5 to 65 ms uncached for five names through systemd-resolved\n# on the machine this was written on, 0 to 1 ms cached (dig's query\n# times) - where the race saves what an address that drops SYNs costs,\n# the whole timeout. Asked in turn, an NXDOMAIN for A spares the AAAA\n# query, as it does for most of a search list's candidates.\n#\n# A query is one UDP datagram from a fresh socket (so a fresh source\n# port), with an ID from `Random::Secure`, to each server in turn, each\n# waited for `timeout_ms`, and the round repeated `attempts` times. A\n# datagram that does not carry the ID and the question is ignored and the\n# wait goes on; one from any other address never arrives, because the\n# socket is connected to the server. An answer with TC set is asked again\n# over TCP. SERVFAIL, REFUSED and a server that does not answer move on to\n# the next server; NXDOMAIN is the answer. An answer that cannot be read\n# is ignored like a forgery, and is the failure reported if nothing\n# better arrives before the timeout.\n#\n# Encrypted:\n# `encrypted(upstreams)` is the same resolver - hosts file, search list,\n# `ndots`, timeout and attempts - asking `DnsUpstream`s instead of its\n# plaintext servers: DNS over TLS or over HTTPS, std/secure_dns's\n# `DnsOverTls` and `DnsOverHttps`. Each upstream is asked in turn, the\n# round repeated `attempts` times, as the servers are; A and AAAA go in\n# one exchange. An answer over one is held to more than a datagram is:\n# the ID and the question as ever, and no TC and no byte past the message,\n# since a stream has room for any answer and its frame is the message.\n# The plaintext servers are not asked at all, not even when every\n# upstream is down, unless the caller passes *plaintext_fallback*: RFC\n# 8310 5's strict profile, where a lookup that cannot be private fails,\n# is the default, because falling back hands the very question the\n# program meant to keep private to anyone on the path, and a program\n# that would rather have an answer says so. With *plaintext_fallback*\n# (the opportunistic profile) the plaintext servers are asked only when\n# no upstream could be: one that answered, SERVFAIL included, has spoken.\n# One task at a time per resolver with upstreams: an upstream holds its\n# connection, as an `HTTPClient` does.\npub class Resolver\n protected def ask_upstreams_instead(upstreams : Array(DnsUpstream), plaintext_fallback : Bool) : Nil\n def attempts : Int32\n # *records* by ascending priority, those of one priority shuffled.\n def self.by_priority(records : Array(SvcbRecord)) : Array(SvcbRecord)\n # Every upstream's connections closed.\n def close : Nil\n # This resolver asking *upstreams* instead of its plaintext servers,\n # which are asked only with *plaintext_fallback*, and then only when no\n # upstream could be (see the class).\n def encrypted(upstreams : Array(DnsUpstream), plaintext_fallback : Bool = false) : Resolver\n def encrypted(upstream : DnsUpstream, plaintext_fallback : Bool = false) : Resolver\n # The machine's resolver - its hosts file and search list - asking\n # *upstreams*; its name servers only with *plaintext_fallback*.\n def self.encrypted(upstreams : Array(DnsUpstream), plaintext_fallback : Bool = false) : Resolver\n def self.encrypted(upstream : DnsUpstream, plaintext_fallback : Bool = false) : Resolver\n # A resolver from a parsed resolv.conf and a hosts file's text. A\n # `nameserver` line this resolver cannot use - an IPv6 address with a\n # zone, a typo - is skipped as glibc skips one; with none left, the\n # servers a machine without a configuration is assumed to run.\n def self.from_config(config : ResolvConf, hosts : String = \"\") : Resolver\n # A resolver from a resolv.conf and a hosts file at these paths. A file\n # that is not there reads as empty, as it does to the C library.\n def self.from_files(resolv_conf : String, hosts : String) : Resolver\n # The hosts file's text, read before any server is asked.\n def hosts : String\n # The HTTPS records (RFC 9460 section 9) of the origin `https://host:port`,\n # by SVCB resolution (section 3): the question is *host* for port 443\n # and `_port._https.host` for any other (section 9.1); an AliasMode\n # answer is followed to its target's HTTPS records, at most\n # `SvcbAnswer::MAX_ALIASES` of them, and CNAMEs on the way as `txt`\n # follows them; and the ServiceMode records found are answered by\n # priority, those of one priority in a random order (section 2.4.1).\n # The name is asked as written, as for `txt`, over the upstreams when\n # the resolver has them.\n #\n # Of an AliasMode set one record is followed, at random where there are\n # several (section 2.4.2), and any ServiceMode record beside it is\n # passed over (section 2.4.1). A target that has no HTTPS records ends\n # resolution there, with none: the answer's `name` is the target, which\n # section 3 still has a client connect to. An answer with a record\n # `SvcbRecord.parse` refuses is the whole set refused, `malformed?`, as\n # section 2.2 has it; so is a chain longer than the bound. NXDOMAIN and\n # NODATA for the origin's own name are `not_found?` and `no_address?`,\n # as they are for an address.\n def https_records(host : String, port : Int32 = 443) : SvcbAnswer | DnsError | Cancelled\n # *nameservers* are addresses, each with an optional port:\n # `\"192.0.2.1\"`, `\"192.0.2.1:5353\"`, `\"2001:db8::1\"`,\n # `\"[2001:db8::1]:5353\"`. One that is not is refused here, by name: it is\n # the program's own mistake. None at all is a resolver that reads only\n # *hosts*.\n def initialize(nameservers : Array(String), search : Array(String) = [] of String, ndots : Int32 = 1, timeout_ms : Int32 = 5000, attempts : Int32 = 2, hosts : String = \"\")\n # What NODATA for *type* says the name lacks: its records, by name, for\n # the types asked other than as addresses.\n def self.missing(type : Int32) : String\n def nameservers : Array(String)\n def ndots : Int32\n # The monotonic clock in milliseconds: the runtime's, which its sleep\n # queue keeps deadlines by. A target without the runtime has no socket\n # to wait on either (`__sys_dgram_socket` refuses there), so nothing\n # there reads a time.\n def self.now_ms : Int64\n # The answer to one question over plaintext: each server in turn, the\n # round repeated `attempts` times, until one answers NOERROR or\n # NXDOMAIN. The failure answered otherwise is the last server's.\n protected def plaintext(name : String, type : Int32) : Tuple(DnsMessage, String) | DnsError | Cancelled\n # Whether `nameservers` are asked when no upstream could be (RFC 8310's\n # opportunistic profile); false, the strict one, by default.\n def plaintext_fallback : Bool\n def self.rcode_name(rcode : Int32) : String\n # What an answer's RCODE makes of it: the message for NOERROR, and the\n # failure it says otherwise.\n def self.read_rcode(message : DnsMessage, name : String, label : String) : Tuple(DnsMessage, String) | DnsError\n # An answer that came over a stream, read: see `ask_upstream`.\n def self.read_streamed(bytes : Bytes, id : UInt16, name : String, type : Int32, label : String) : Tuple(DnsMessage, String) | DnsError\n # Every address *host* stands for: see the class's comment for the\n # order things are asked in.\n def resolve(host : String) : Array(IPAddress) | DnsError | Cancelled\n def search : Array(String)\n # A name server written as an address with an optional port, or the\n # sentence saying why it is not one.\n def self.server(text : String) : NameServer | String\n # The machine's resolver, read afresh each time: a configuration the\n # network manager rewrote is the one the next lookup uses.\n def self.system : Resolver\n def timeout_ms : Int32\n # The TXT records of *name*, each one's character-strings joined\n # (`DnsRecord.character_strings`), in the order the answer gave them,\n # CNAMEs followed as `resolve` follows them. The name is asked as\n # written - no hosts file, no search list - since a TXT name\n # (`_acme-challenge.example.com`, a domain's SPF) is written in full.\n def txt(name : String) : Array(String) | DnsError | Cancelled\n # The encrypted upstreams asked instead of `nameservers`; none for a\n # plaintext resolver.\n def upstreams : Array(DnsUpstream)\nend\n\n# A connection's own failure: which operation, and the platform's error\n# number for why - or, for a host name that could not be turned into an\n# address, the resolver's `DnsError`, and no number (`code` 0); or, for a\n# protocol layered on the connection (TLS), that protocol's own sentence,\n# and no number either; or, for a name whose addresses were all tried and\n# none connected, every one of them with how its attempt ended.\npub struct SocketError\n def code : Int32\n # The peer is gone: it reset the connection, or closed it and the write\n # found the pipe broken, or the connection was aborted.\n def disconnected? : ::Bool\n # Why the host's name did not resolve; nil for every other failure.\n def dns_error : DnsError | ::Nil\n # A connect to a name none of whose addresses took the connection:\n # *code* is what `refused?` and `timed_out?` read, and *tried* the\n # sentence naming every address.\n def self.every_address(operation : String, code : Int32, tried : String) : SocketError\n def initialize(operation : String, code : Int32, dns_error : DnsError | ::Nil, layer_reason : String | ::Nil, tried : String | ::Nil)\n def initialize(operation : String, code : Int32, dns_error : DnsError | ::Nil, layer_reason : String | ::Nil)\n def initialize(operation : String, code : Int32, dns_error : DnsError | ::Nil)\n def initialize(operation : String, code : Int32)\n # What the protocol over the connection said went wrong; nil otherwise.\n def layer_reason : String | ::Nil\n # A failure of the protocol over the connection, in its words: `cannot\n # read from example.com: tls: a record that does not decrypt`.\n def self.layered(operation : String, reason : String) : SocketError\n def operation : String\n def reason : String\n def refused? : ::Bool\n # A wait for the peer that ran past its socket's timeout, carrying the\n # platform's own number for it so `reason` and `timed_out?` read it as\n # they read the kernel's.\n def self.timed_out(operation : String) : SocketError\n # The peer did not answer in time: a connect the network timed out, or a\n # read or write past its socket's timeout.\n def timed_out? : ::Bool\n # For a connect to a name that stood for more than one address: each\n # address in the order it was tried and how its attempt ended -\n # `[2001:db8::1]:443 timed out; 192.0.2.1:443 connection refused` - which\n # `reason` answers. Nil for every other failure.\n def tried : String | ::Nil\n def unreachable? : ::Bool\n # The host was a name, and no address came of it: `dns_error` says why\n # (`not_found?` for NXDOMAIN, `server_failure?` for SERVFAIL, ...).\n def unresolved? : ::Bool\nend\n\n# A socket as a stream, for what is written against streams: `read_line`,\n# `puts`, `IyiIO.copy` between it and a file or a pipe. Its reads and\n# writes are the socket's own, which park the task and keep its\n# timeouts. A stream has no error to answer, so a read the socket refuses\n# ends the stream, `read_failed?` true, and a write it refuses panics,\n# naming the reason; a program that answers those reads the socket.\n# Writes are buffered: `flush`, or `IyiIO.copy`, which flushes, sends\n# them. Closing it closes the socket.\npub class SocketIO < ::IyiIO\n def initialize(socket : IyiSocket, buffer_size : Int32 = 4096)\n def socket : IyiSocket\nend\n\nimpl Error for SocketError\n def message : String\nend\n" }, { "name": "std/sse", "path": "src/std/sse.iyi", - "sourceSha256": "0655aa4b8fe9e1461e7ca318b7a6067435c790f2e52686897211abe79cb83269", + "sourceSha256": "6e80fccb4686258b20300faefc16c3e466feb7e834c208af77846818eac77af7", "surface": "module std/sse\n\n# One dispatched event: its type (`message` unless the stream named one\n# with `event:`), its data, and the last event ID as it stood when it was\n# dispatched, which an event without an `id:` field inherits.\npub struct Event\n def data : String\n def event : String\n def id : String\n def initialize(event : String, data : String, id : String)\nend\n\n# §9.2.6's interpretation of an event stream, fed its bytes as they come:\n# `feed(bytes) { |event| ... }` yields each event a blank line\n# dispatches, and keeps what a piece ended inside - a line, an event, the\n# CR of a CRLF - for the next. `finish` discards it, as the end of a\n# stream does. `last_event_id` is the source's last event ID string,\n# set at each blank line; `retry` the reconnection time the stream set,\n# nil until it sets one; `dispatched` the events yielded so far.\npub class EventParser\n # A `retry` value: ASCII digits and nothing else, base ten, saturating\n # at `MAX_RETRY`; nil for anything else, the empty value included.\n protected def self.digits(text : String) : Int32 | ::Nil\n def dispatched : Int64\n def feed(text : String, & : (Event -> Nil)) : Nil\n # The end of the stream: a line or an event not yet ended is discarded,\n # not dispatched. The last event ID and the reconnection time stay.\n def finish : Nil\n # A parser for a stream whose last event ID is *last_event_id* so far:\n # a reconnection's, which carries the one before it (see the module's\n # header for why).\n def initialize(last_event_id : String = \"\")\n def last_event_id : String\n def retry : Int32 | ::Nil\nend\n\n# What `SSE.stream`'s block writes with: an event a `send`, a comment a\n# `comment`, each one chunk sent at once. The first write that fails -\n# the client went - is kept, and it and every write after it answer it;\n# `gone?` says whether one has.\npub class EventWriter\n # A comment, a `:` line per line of *text*: read past by every client,\n # and a write that finds a client gone.\n def comment(text : String = \"\") : Nil | Cancelled | SocketError\n # Whether a write found the client gone.\n def gone? : Bool\n def initialize(writer : BodyWriter)\n # *text* cut at each CRLF, CR and LF: one line for a text without one,\n # an empty last line for a text that ends with one.\n protected def self.lines(text : String) : Array(String)\n # One event: `event:`, `id:` and `retry:` when given, *data* a `data:`\n # line per line (CRLF, CR and LF each end one, and each arrives as LF),\n # then the blank line that dispatches it. An empty *data* is an event\n # whose data is empty; an empty *id* resets the client's last event ID.\n def send(data : String, event : String | ::Nil = nil, id : String | ::Nil = nil, retry : Int32 | ::Nil = nil) : Nil | Cancelled | SocketError\n protected def self.utf8(text : String, what : String) : Nil\nend\n\npub struct SSE\n # Whether a `Content-Type` is `text/event-stream`, in any case, with any\n # parameters.\n def self.event_stream?(type : String) : Bool\n # One connection's answer read: true when the block stopped, false when\n # the stream ended, or why it could not be read.\n protected def self.follow(response : Response, parser : EventParser, block : Proc(Event, Bool)) : Bool | SSEError | Cancelled | SocketError | HTTPError\n def initialize\n # The event stream at *url*, each event handed to *block*, which answers\n # true to go on and false to stop (nil, the connection closed). A\n # connection that drops or ends is made again after the reconnection\n # time - *retry* ms until the stream sets its own - with the last event\n # ID (*last_event_id* to start with, to resume a stream) in\n # `Last-Event-ID`. Once *reconnects* + 1 connections in a row have\n # brought no event, the last one's failure is the answer. An answer\n # that is not a 200 `text/event-stream` is an `SSEError` at once.\n # *headers* go with every request; `Accept` and `Last-Event-ID` are the\n # stream's own and are refused there.\n def self.open(url : String, headers : Hash(String, String) = {} of String => String, last_event_id : String | ::Nil = nil, reconnects : Int32 = RECONNECTS, retry : Int32 = RETRY, &block : (Event -> Bool)) : Nil | Cancelled | SocketError | HTTPError | SSEError\n # The answer to a request for an event stream: a 200, `text/event-stream`,\n # `Cache-Control: no-cache` unless *headers* names its own, whose body\n # *block* writes through an `EventWriter` as it goes. The stream ends\n # when the block returns. A `Content-Type` in *headers* is refused.\n def self.stream(headers : Hash(String, String) = {} of String => String, &block : (EventWriter -> Nil)) : Response\nend\n\n# What the other end did that fails an event stream (§9.2.3), as a value:\n# an answer that is not a 200, one that is not `text/event-stream`, or a\n# stream that kept ending with no event until the client gave up.\n# `status` is the answer's. `message` says it is SSE's.\npub struct SSEError\n def initialize(reason : String, status : Int32 = 200)\n def reason : String\n def status : Int32\n def to_s : String\nend\n\nimpl Error for SSEError\n def message : String\nend\n" }, { "name": "std/static_array", "path": "src/std/static_array.iyi", - "sourceSha256": "71ecdb56f0e7a71d3fecc9fa5d825e11229689898ebf2ad2299f5121a5c49bba", + "sourceSha256": "20577aa636cdc2ab2897e4648e9e0a5c5b3aecbe9b61c9a3383cca1e32b7cb87", "surface": "module std/static_array\n\npub alias StaticArray = ::StaticArray\n\nstruct ::StaticArray(T, N)\n # Comparison based on lexicographical element order. Written out here: it\n # forwarded to `Slice#<=>`, which the slice module does not have, so any\n # call of this method failed to compile.\n def <=>(other : StaticArray(T, N)) : Int32\n # Equality. Returns true if corresponding elements are equal, where one\n # element type holds the other, as tuples compare (src/iyi/object.iyi).\n # Restricted to `StaticArray(T, N)`, a `StaticArray(Int32, 2)` was taken\n # for a `StaticArray(Int32?, 2)` and not back: the one equality answered\n # true one way and false the other.\n def ==(other : StaticArray(U, M)) : Bool forall U, M\n def ==(other : Object) : Bool\n # Returns the element at the given *index*. Negative indices count backward.\n def [](index : Int) : T\n # Sets the element at *index* to *value*.\n def []=(index : Int, value : T) : T\n # Returns the element at *index*, or nil if out of bounds.\n def []?(index : Int) : T | ::Nil\n def all? : Bool\n def any? : Bool\n def clone : self\n def count(item) : Int32\n def count : Int32\n # Duplicates this static array.\n def dup : self\n # Yields each element in order.\n def each(& : (T -> Nil)) : Nil\n # Yields each element alongside its index.\n def each_with_index(offset : Int = 0, & : (T, Int32 -> Nil)) : Nil\n # Returns true when the static array contains zero elements.\n def empty? : Bool\n # Fills *count* elements starting at *start* with *value*.\n #\n # A negative count and a start outside are refused before anything is\n # filled, as the other library and `Slice#fill` refuse them: an early\n # return for `c <= 0` came first, so `fill(7, 0, -1)` and `fill(7, 9, 0)`\n # of four elements answered the array untouched and said nothing.\n def fill(value : T, start : Int, count : Int) : self\n # Fills the entire static array with *value*.\n def fill(value : T) : self\n # Returns the first element. Raises if empty.\n def first : T\n # Returns the first element or nil if empty.\n def first? : T | ::Nil\n # Hashing follows the `==` above, mixed as `Array#hash` mixes. There was\n # only the other library's `hash(hasher)`, which no table here calls, so\n # `Object#hash` answered the type's id: every `StaticArray(Int32, 2)` was\n # one slot, and 40,000 of them took 13 s to insert where the same pairs\n # as tuples took 3 ms.\n def hash : Int32\n # Returns true if *object* is in the array.\n def includes?(object) : Bool\n # Returns the index of *object*, or nil if not found.\n def index(object, offset : Int = 0) : Int32 | ::Nil\n def inspect(io : IO) : Nil\n def inspect : String\n # Returns the last element. Raises if empty.\n def last : T\n # Returns the last element or nil if empty.\n def last? : T | ::Nil\n # Returns a new StaticArray with mapped elements.\n def map(&block : (T -> U)) : StaticArray(U, N) forall U\n # Returns a new StaticArray with mapped elements and indices.\n def map_with_index(offset : Int = 0, &block : (T, Int32 -> U)) : StaticArray(U, N) forall U\n # Creates a new StaticArray filled by invoking the block for each index.\n def self.new(& : (Int32 -> T)) : self\n # Creates a new StaticArray filled with the given value.\n def self.new(value : T) : self\n def none? : Bool\n def one? : Bool\n # Yields elements in reverse order.\n def reverse_each(& : (T -> Nil)) : Nil\n # Reverses elements in place.\n def reverse_in_place : self\n # Returns the last index of *object*, or nil if not found.\n def rindex(object, offset : Int = N - 1) : Int32 | ::Nil\n # Rotates elements in place by *n* positions. Reduced in `Int64` before it\n # is narrowed, as `index_within` and `BitArray`'s rotation count: `n.to_i`\n # panicked \"arithmetic overflow\" for 3000000001_i64, which rotates four\n # elements by one.\n def rotate_in_place(n : Int = 1) : self\n # Returns the size of self.\n def size : Int32\n # Returns a new sorted static array.\n def sort : StaticArray(T, N)\n def sort(&block : (T, T -> Int32)) : StaticArray(T, N)\n def sort_by(&block : (T -> _)) : StaticArray(T, N)\n # Each key computed once, by `Array#sort_in_place_by`, the merge sort of\n # key and element pairs: a comparison of `yield(a) <=> yield(b)` computed\n # it twice per comparison, 63,992,000 times for 8,000 elements.\n def sort_by_in_place(&block : (T -> _)) : self\n # Sorted by `Array#sort_in_place`, the stable merge sort, and copied\n # back. It was an insertion sort of its own, quadratic: 8,000 elements in\n # reverse order took 31,996,000 comparisons where the array's sort takes\n # under 100,000. Equal elements keep their order, as they did.\n def sort_in_place : self\n # In-place sort with custom block comparator, by the same merge sort.\n def sort_in_place(&block : (T, T -> Int32)) : self\n # Converts this static array into a heap Array.\n def to_a : Array(T)\n # The text `to_s` builds, written as `BitArray#to_s(io)` writes it. It\n # asked each element for `inspect(io)`, which no prelude type has, and\n # `IyiIO` for `<<`, which only `import std/io` gives it: `to_s(STDOUT)`\n # of a `StaticArray(Int32, 3)` did not compile, and `inspect(io)` with it.\n def to_s(io : IO) : Nil\n # Into one builder: adding each element to the text so far copied that\n # text once per element, and 10,000 elements took 1.4 s where `to_a.to_s`\n # of the same took 1 ms.\n def to_s : String\n # Returns a Slice backed by this static array.\n def to_slice : Std::Slice::Slice(T)\n # Returns a pointer to this static array's data.\n def to_unsafe : Pointer(T)\n # Returns the element at *index* without bounds checking.\n def unsafe_fetch(index : Int) : T\n # Sets the element at *index* to *value* without bounds checking.\n def unsafe_put(index : Int, value : T) : T\n def unstable_sort : StaticArray(T, N)\n def unstable_sort(&block : (T, T -> Int32)) : StaticArray(T, N)\n def unstable_sort_by(&block : (T -> _)) : StaticArray(T, N)\n def unstable_sort_by_in_place(&block : (T -> _)) : self\n def unstable_sort_in_place : self\n def unstable_sort_in_place(&block : (T, T -> Int32)) : self\n macro [](*args)\nend\n" }, { @@ -562,67 +568,67 @@ { "name": "std/string_pool", "path": "src/std/string_pool.iyi", - "sourceSha256": "82ac6a65e5005b1283ab9ac80a3331935d6c40ec9feb263bd91158f022812f03", + "sourceSha256": "65decd7c3975ef73b997d2c29575fed6b60eb2bb9018fc946d49746b65b33375", "surface": "module std/string_pool\n\npub alias Bytes = Std::Slice::Bytes\n\npub class StringPool\n def capacity : Int32\n def empty? : Bool\n def get(str : Pointer(UInt8), len : Int32) : String\n def get(slice : Bytes) : String\n def get(str : String) : String\n def get?(str : Pointer(UInt8), len : Int32) : String | ::Nil\n def get?(slice : Bytes) : String | ::Nil\n def get?(str : String) : String | ::Nil\n def initialize(initial_capacity : Int32 = 8)\n def rehash : Nil\n def size : Int32\nend\n" }, { "name": "std/string_scanner", "path": "src/std/string_scanner.iyi", - "sourceSha256": "2841a382ac9f90171e907ac3952c550f940a51443e417e3edb02ab1252b13dc4", + "sourceSha256": "73b743c7bb88929cc88d07649f2665b06dadc30fb614a4cb45fed3be9d7568e1", "surface": "module std/string_scanner\n\npub class StringScanner\n # Group *n* of the last match, the whole match being 0; a negative *n*\n # counts from the last group. No last match, a group it does not have and\n # one that took no part are mistakes, and panic with which.\n def [](n : Int32) : String\n # The group called *name* of the last match.\n def [](name : String) : String\n # Group *n* of the last match, or nil for any of the reasons `[]` panics.\n def []?(n : Int32) : String | ::Nil\n # The group called *name* of the last match, or nil.\n def []?(name : String) : String | ::Nil\n # What `scan` would answer, without moving; the match is still the last.\n def check(pattern : Regex | String | Char | Int32) : String | ::Nil\n # What `scan_until` would answer, without moving.\n def check_until(pattern : Regex | String | Char) : String | ::Nil\n # Whether the offset is at the end of the string.\n def eos? : Bool\n def initialize(string : String)\n # `#`, as the other library writes it: the\n # offset, the length, and five characters around the offset, two before\n # it where there are, with `‣` at the offset and `…` where the string\n # goes on. The two numbers are bytes here, as `offset` is.\n def inspect : String\n # Whether the last scan, skip, check or match matched.\n def matched? : Bool\n # How many bytes *pattern* matches at the offset, without moving, or nil.\n def matches?(pattern : Regex | String | Char) : Int32 | ::Nil\n # The offset, in bytes.\n def offset : Int32\n # Moves the offset to byte *position*: the end of the string is the\n # furthest it goes, and a position inside a character is refused, as\n # every read after it would start on half of one.\n def offset=(position : Int32) : Int32\n # Up to *count* characters from the offset, without moving: fewer at the\n # end of the string. The last match is left as it was.\n def peek(count : Int32) : String\n # Back to the start, with no last match.\n def reset : Nil\n # The string from the offset on.\n def rest : String\n # Matches *pattern* at the offset and moves past it, answering what\n # matched, or nil without moving.\n def scan(pattern : Regex | String | Char | Int32) : String | ::Nil\n # Searches for *pattern* from the offset and moves past the match,\n # answering everything from the offset to the match's end.\n def scan_until(pattern : Regex | String | Char) : String | ::Nil\n # `scan`, answering how many bytes the offset moved.\n def skip(pattern : Regex | String | Char | Int32) : Int32 | ::Nil\n # `scan_until`, answering how many bytes the offset moved.\n def skip_until(pattern : Regex | String | Char) : Int32 | ::Nil\n # The string being scanned.\n def string : String\n # On to the end, with no last match.\n def terminate : Nil\nend\n" }, { "name": "std/symbol", "path": "src/std/symbol.iyi", - "sourceSha256": "14fb4c4900a328c01fddc8924bded404524574c20d729c00c96bac261d74bb08", + "sourceSha256": "ed815a3b21404c27fb049aa5bfff5abbfbc08637d148741f1f28d0869c42e0e4", "surface": "module std/symbol\n\nstruct ::Symbol\n def <(other : Symbol) : ::Bool\n def <=(other : Symbol) : ::Bool\n # Compares symbol with other based on string ordering.\n def <=>(other : Symbol) : Int32\n def >(other : Symbol) : ::Bool\n def >=(other : Symbol) : ::Bool\n def clone : Symbol\n # Body of a double-quoted inspect/named-arg form, escaped the way the\n # other library's `String#inspect` escapes it: quotes and backslashes,\n # `#{` (which would interpolate), the named controls (`\\a \\b \\e \\f \\n\n # \\r \\t \\v`) and every other ASCII control or DEL as `\\u00XX`. Only\n # `\"`, `\\`, `\\n`, `\\r`, `\\t` and NUL were escaped, so `:\"a\\eb\".inspect`\n # carried a raw escape byte and NUL read `\\0` where the other says\n # `\\u0000`.\n def self.escape_quoted_body(string : String) : String\n def inspect(io : IyiIO) : ::Nil\n def inspect : String\n # True when *string* cannot be written as an unquoted symbol literal.\n # Identifiers may end in `?` or `!` (SPEC.md III.1.7 exempts symbol\n # literals from the identifier bang ban). Operators in the list below\n # are also unquoted.\n def self.needs_quotes?(string : String) : ::Bool\n def self.needs_quotes_for_named_argument?(string : String) : ::Bool\n def self.quote_for_named_argument(io : IyiIO, string : String) : ::Nil\n def self.quote_for_named_argument(string : String) : String\n def to_s(io : IyiIO) : ::Nil\nend\n" }, { "name": "std/system", "path": "src/std/system.iyi", - "sourceSha256": "9b5a4be0a353856e85b8658cd7133a87a98b4215caa6aebdf83ae2583f06761c", + "sourceSha256": "611b84f17fffc7a7d979ada725f14b26741e8404d05c2a5b8ccd832cfd9b5dee", "surface": "module std/system\n\npub struct System\n # How many CPUs this process may run on: never more than the machine\n # has, fewer when the process is held to some of them.\n def self.cpu_count : Int32\n # The name of the machine, as the kernel holds it: `\"build-07\"`, or\n # `\"host.example.org\"` where it was set to a fully qualified name.\n def self.hostname : String\n def initialize\n # The size of a memory page in bytes: 4096 on most machines, 16384 on\n # Apple silicon and on some aarch64 Linux kernels, 65536 on others.\n def self.page_size : Int32\nend\n" }, { "name": "std/system_roots", "path": "src/std/system_roots.iyi", - "sourceSha256": "cd7c8802a49ce259292e1d917cbcf5ef614874a8b5e21a7f528cb6830ea07ad6", + "sourceSha256": "c1119e7739201e8971c97a37e5d3a1ebfcbc51de13b5fac547a4c3e06d4aafdf", "surface": "module std/system_roots\n\npub struct SystemRoots\n # The certificates, each once, in the order the store gave them.\n def certificates : Array(Certificate)\n def initialize(certificates : Array(Certificate), unreadable : Int32, source : String)\n # The operating system's trusted roots, or why there are none.\n def self.load : SystemRoots | X509Error\n # Where they came from: files, or the platform's store by name.\n def source : String\n # How many entries std/x509 could not read.\n def unreadable : Int32\nend\n" }, { "name": "std/tar", "path": "src/std/tar.iyi", - "sourceSha256": "10b28dab507a035213abe820c3954816c7bbc86895e62bc9e4cf82d92e4d4f64", + "sourceSha256": "ddb70e77b793f1a0feca94122443002ea0beca540e22c10c8f02e7c6aa042192", "surface": "module std/tar\n\n# One entry's header, read by `TarReader#next_entry`; its data is read\n# with `read` and `read_all` until the reader moves on.\npub class TarEntry\n # How many bytes of data follow the header: the size, or none for a\n # type that carries none.\n def data_size : Int64\n def devmajor : Int64\n def devminor : Int64\n def directory? : Bool\n def file? : Bool\n def format : TarFormat\n def gid : Int64\n def gname : String\n def hardlink? : Bool\n def index : Int32\n def initialize(reader : TarReader, index : Int32, name : String, linkname : String, size : Int64, mode : Int32, uid : Int64, gid : Int64, uname : String, gname : String, mtime : Time, typeflag : UInt8, devmajor : Int64, devminor : Int64, pax : Hash(String, String), format : TarFormat, offset : Int64)\n # A link's target, the same way: pax `linkpath`, GNU `K`, the field.\n def linkname : String\n def mode : Int32\n def mtime : Time\n # The name: a pax `path`, a GNU long name, the ustar prefix and name\n # joined by `/`, or the name field, the first of these there is.\n def name : String\n # Where its header is, from the start of the (uncompressed) archive.\n def offset : Int64\n # Every pax record given to this entry, the global ones under the ones\n # of its own.\n def pax : Hash(String, String)\n # Up to *max* bytes of the data, \"\" at its end; or what is wrong with\n # the archive. Only until the reader's next `next_entry`: a read after\n # it is a panic, as is a *max* below 1.\n def read(max : Int32 = 65536) : String | TarError\n # The whole of the data (what is left of it).\n def read_all : String | TarError\n # The size the header (or a pax `size`) declares. Links, devices,\n # FIFOs and directories carry no data whatever it says, as POSIX\n # specifies and Go reads them.\n def size : Int64\n def symlink? : Bool\n def type : TarType\n # What the typeflag names, for a sentence: \"regular file\", \"FIFO\",\n # \"GNU sparse file\", \"type 'Q'\".\n def type_name : String\n # The typeflag byte as written.\n def typeflag : UInt8\n def uid : Int64\n def uname : String\nend\n\n# What is wrong with an archive: `message` is the sentence, which names\n# the entry, or the header's offset in the (uncompressed) stream.\npub struct TarError\n def initialize(message : String)\n def to_s : String\nend\n\n# Which header an entry was read from: `Pax` when a pax record was folded\n# into it, else by the header's magic.\npub enum TarFormat : Int32\n V7 = 0\n Ustar = 1\n Gnu = 2\n Pax = 3\nend\n\n# An archive read entry by entry.\n#\n# ```\n# tar = TarReader.new(File.open(\"src.tar.gz\"))\n# while entry = tar.next_entry.or_panic\n# puts \"#{entry.name} #{entry.size}\"\n# text = entry.read_all.or_panic if entry.name == \"README\"\n# end\n# ```\npub class TarReader\n # Whether the stream is bzip2; known once the first entry is asked for.\n def bzip2? : Bool\n # For `TarEntry#read`.\n def entry_read(entry : TarEntry, max : Int32) : String | TarError\n # For `TarEntry#read_all`.\n def entry_read_all(entry : TarEntry) : String | TarError\n # Every entry written under *dir* (made if it is not there), as it is\n # read; see the module's comment for what is refused. Answers the first\n # error, the archive's or the refusal's; a file cut short is deleted.\n def extract(dir : String) : Nil | TarError\n # Whether the stream is gzip; known once the first entry is asked for.\n def gzip? : Bool\n # The archive *io* holds from where it is, plain or gzip.\n def initialize(io : IyiIO)\n # The next entry, its header read and checked; nil at the end. The\n # data of the entry before, as much as was not read, is read past.\n def next_entry : TarEntry | Nil | TarError\n # *path*'s parts, without empty ones and `.`.\n def self.parts(path : String) : Array(String)\n # A pax header's records into *into*. A global record with no value\n # takes the key away; an entry's keeps it, empty, which stands for the\n # header's own field.\n def self.records(data : String, at : Int64, into : Hash(String, String), global : Bool) : Nil | TarError\n # Why a symbolic link *name* to *target* may not be made under a\n # directory, or nil when it may: the target must be relative, may climb\n # with `..` no higher than the link's own directory, and only before\n # its first other part - after one, the part could be a link itself and\n # the `..` would climb from wherever that points.\n def self.unsafe_link(name : String, target : String, windows : Bool = false) : String | ::Nil\n # Why *name* may not be written under a directory, or nil when it may.\n # *windows* refuses the separator and the drive Windows reads in it.\n def self.unsafe_name(name : String, windows : Bool = false) : String | ::Nil\n # A path Windows reads from a root rather than from where it is put:\n # `\\x`, or a drive with its separator, `C:\\x` and `C:/x`. Refused anyway\n # for its `\\` or its `:`, but the sentence names what it is. `C:x`, the\n # drive's own working directory, is not one, and is refused for its `:`.\n def self.windows_absolute?(path : String) : Bool\n # Whether the stream is xz; known once the first entry is asked for.\n def xz? : Bool\nend\n\n# What an entry is, by its typeflag. `Other` is any typeflag this module\n# has no name for; `TarEntry#type_name` names it.\npub enum TarType : Int32\n Regular = 0\n Hardlink = 1\n Symlink = 2\n CharDevice = 3\n BlockDevice = 4\n Directory = 5\n Fifo = 6\n Contiguous = 7\n Other = 8\nend\n\n# An archive written to an `IyiIO`, an entry at a time.\n#\n# ```\n# File.open(\"out.tar.gz\", \"w\") do |io|\n# tar = TarWriter.new(io, gzip: true)\n# tar.add(\"README\", text)\n# tar.add_directory(\"bin\")\n# tar.add(\"bin/run\", script, mode: 0o755)\n# tar.add_symlink(\"latest\", \"bin/run\")\n# tar.finish\n# end\n# ```\n#\n# What is wrong with the calls is a panic: a name that is empty, holds a\n# NUL or is absolute; a link without a target; a negative size, id or\n# a mode outside 0 to 0o7777; a source that ends before the size it was\n# said to hold; an `add` after `finish`, and `finish` twice.\npub class TarWriter\n # *data* as the regular file *name*.\n def add(name : String, data : String, mode : Int32 = 420, mtime : Time = Time.utc, uid : Int64 = 0_i64, gid : Int64 = 0_i64, uname : String = \"\", gname : String = \"\") : Nil\n # *size* bytes of *source* as the regular file *name*, copied as they\n # are read. *source* is not closed.\n def add(name : String, source : IyiIO, size : Int64, mode : Int32 = 420, mtime : Time = Time.utc, uid : Int64 = 0_i64, gid : Int64 = 0_i64, uname : String = \"\", gname : String = \"\") : Nil\n # A directory: *name* with a `/` after it if it has none.\n def add_directory(name : String, mode : Int32 = 493, mtime : Time = Time.utc, uid : Int64 = 0_i64, gid : Int64 = 0_i64, uname : String = \"\", gname : String = \"\") : Nil\n # A hard link *name* to the entry *target*, earlier in the archive.\n def add_hardlink(name : String, target : String, mode : Int32 = 420, mtime : Time = Time.utc, uid : Int64 = 0_i64, gid : Int64 = 0_i64, uname : String = \"\", gname : String = \"\") : Nil\n # A symbolic link *name* to *target*, which is written as it is given.\n def add_symlink(name : String, target : String, mode : Int32 = 511, mtime : Time = Time.utc, uid : Int64 = 0_i64, gid : Int64 = 0_i64, uname : String = \"\", gname : String = \"\") : Nil\n # The end: two zero blocks, then zeros to a 10,240-byte record; then\n # the gzip trailer, if gzip; then *io* is flushed. Once only.\n def finish : Nil\n # *gzip* writes the archive through `Deflater` in the gzip envelope.\n def initialize(io : IyiIO, gzip : Bool = false)\n # One pax record: its length counts its own digits.\n def self.record(key : String, value : String) : String\nend\n\nimpl Error for TarError\n def message : String\nend\n" }, { "name": "std/term", "path": "src/std/term.iyi", - "sourceSha256": "a9f1a8c375ec578a1ee1144a0097a2f0810f55fe8cbe5dbbf45943d074da1d54", - "surface": "module std/term\n\npub struct Term\n def initialize\n # Runs the block with echo off on *io*'s terminal and answers its value;\n # lines are still read as lines and Ctrl-C is still a signal. The\n # previous mode is restored when the block ends, however it ends. A\n # `TermError`, and the block not run, when *io* is not a terminal.\n def self.no_echo(io : IO = STDIN, &block : (-> T)) : T | TermError forall T\n # Runs the block with *io*'s terminal in raw mode and answers its value:\n # every byte is the program's as it is typed — no line to wait for, no\n # echo, no signal from Ctrl-C, no CR to NL — and output is written\n # without NL becoming CR NL. The previous mode is restored when the\n # block ends, however it ends. A `TermError`, and the block not run,\n # when *io* is not a terminal or the terminal refuses the change.\n def self.raw(io : IO = STDIN, &block : (-> T)) : T | TermError forall T\n # Writes *prompt* to standard error and reads a line from standard input\n # with echo off, then writes the newline the terminal did not echo.\n # Answers the line without its line ending, or nil at the end of the\n # input. Standard input that is not a terminal is read as it is.\n def self.read_password(prompt : String = \"\") : String | ::Nil\n # `{rows, columns}` of the terminal *io* is, or nil.\n def self.size(io : IO) : ::Tuple(Int32, Int32) | ::Nil\n def self.size(fd : Int32) : ::Tuple(Int32, Int32) | ::Nil\n def self.size(fd : Int64) : ::Tuple(Int32, Int32) | ::Nil\n # `{rows, columns}` of the terminal on standard output, standard error\n # or standard input, the first that is one; nil when none is, or when\n # the terminal does not know its size (0 by 0).\n def self.size : ::Tuple(Int32, Int32) | ::Nil\n # Whether *io* is a terminal.\n def self.tty?(io : IO) : Bool\n # Whether descriptor *fd* is a terminal — on Windows, a standard\n # stream's number or a handle's value, as `IO#handle` answers it.\n def self.tty?(fd : Int32) : Bool\n def self.tty?(fd : Int64) : Bool\nend\n\n# A terminal that could not be put in the mode asked for, and the\n# platform's error number for why: errno on Linux and darwin, the\n# `GetLastError` code on Windows.\npub struct TermError\n def code : Int32\n def fd : Int64\n def initialize(operation : String, fd : Int64, code : Int32, terminal : Bool)\n # The stream is not a terminal: ENOTTY (25 on Linux and darwin) from\n # TCGETS or `tcgetattr`; on Windows `GetConsoleMode` refusing it at all,\n # which is the test for a console whatever code it leaves.\n def not_a_terminal? : Bool\n def operation : String\n def reason : String\n def stream : String\nend\n\nimpl Error for TermError\n def message : String\nend\n" + "sourceSha256": "874ea1c686767570b1a9e389786aca5fba4775be73500d4856b0c0427966669a", + "surface": "module std/term\n\npub struct Term\n def initialize\n # Runs the block with echo off on *io*'s terminal and answers its value;\n # lines are still read as lines and Ctrl-C is still a signal. The\n # previous mode is restored when the block ends, however it ends. A\n # `TermError`, and the block not run, when *io* is not a terminal.\n def self.no_echo(io : IO = STDIN, &block : (-> T)) : T | TermError forall T\n # Runs the block with *io*'s terminal in raw mode and answers its value:\n # every byte is the program's as it is typed — no line to wait for, no\n # echo, no signal from Ctrl-C, no CR to NL — and output is written\n # without NL becoming CR NL. The previous mode is restored when the\n # block ends, however it ends. A `TermError`, and the block not run,\n # when *io* is not a terminal or the terminal refuses the change.\n def self.raw(io : IO = STDIN, &block : (-> T)) : T | TermError forall T\n # Turns echo off, writes *prompt* to standard error and reads a line\n # from standard input, then writes the newline the terminal did not\n # echo. Answers the line without its line ending, or nil at the end of\n # the input. Standard input that is not a terminal is read as it is.\n def self.read_password(prompt : String = \"\") : String | ::Nil\n # `{rows, columns}` of the terminal *io* is, or nil.\n def self.size(io : IO) : ::Tuple(Int32, Int32) | ::Nil\n def self.size(fd : Int32) : ::Tuple(Int32, Int32) | ::Nil\n def self.size(fd : Int64) : ::Tuple(Int32, Int32) | ::Nil\n # `{rows, columns}` of the terminal on standard output, standard error\n # or standard input, the first that is one; nil when none is, or when\n # the terminal does not know its size (0 by 0).\n def self.size : ::Tuple(Int32, Int32) | ::Nil\n # Whether *io* is a terminal.\n def self.tty?(io : IO) : Bool\n # Whether descriptor *fd* is a terminal — on Windows, a standard\n # stream's number or a handle's value, as `IO#handle` answers it.\n def self.tty?(fd : Int32) : Bool\n def self.tty?(fd : Int64) : Bool\nend\n\n# A terminal that could not be put in the mode asked for, and the\n# platform's error number for why: errno on Linux and darwin, the\n# `GetLastError` code on Windows.\npub struct TermError\n def code : Int32\n def fd : Int64\n def initialize(operation : String, fd : Int64, code : Int32, terminal : Bool)\n # The stream is not a terminal: ENOTTY (25 on Linux and darwin) from\n # TCGETS or `tcgetattr`; on Windows `GetConsoleMode` refusing it at all,\n # which is the test for a console whatever code it leaves.\n def not_a_terminal? : Bool\n def operation : String\n def reason : String\n def stream : String\nend\n\nimpl Error for TermError\n def message : String\nend\n" }, { "name": "std/text", "path": "src/std/text.iyi", - "sourceSha256": "e5ac6a08ed71d3404f2d07bdc5ed4dd67e1d9d0a2d29b5ee6056198e73276074", + "sourceSha256": "cb8f3075bc5976d7b644d273534fed32b3e969f71608d45fe3407d4e3e97c02d", "surface": "module std/text\n\nclass ::Array(T)\n def join(separator : Char) : String\nend\n\nstruct ::Char\n def ascii_alphanumeric? : Bool\n def ascii_letter? : Bool\n def ascii_lowercase? : Bool\n def ascii_number? : Bool\n def ascii_uppercase? : Bool\n def ascii_whitespace? : Bool\n def downcase : Char\n def to_i(base : Int32) : Int32\n def to_i : Int32\n def to_i?(base : Int32) : Int32 | ::Nil\n def to_i? : Int32 | ::Nil\n def upcase : Char\nend\n\nclass ::String\n # `s[1..3]`, `s[-3..-1]`: the bytes a range names, its ends counted from\n # the back when negative, as `[](start, count)` counts them.\n def [](range : Range(Int32, Int32)) : String\n # The whitespace `blank?`, `lstrip` and `rstrip` look through is the\n # prelude's `Char#whitespace?`, decoded, so `strip` with no argument agrees\n # with these and with the other library: `\"\\u3000\".blank?` was false.\n def blank? : Bool\n def bytes : Array(UInt8)\n def capitalize : String\n # The width is in characters, because `size` is and because a centred\n # column is a visual claim: padded by bytes, `\"héllo\".center(9, '.')` came\n # back seven characters wide on one side and eight long in all. The pad\n # travels as its own bytes, so a multi-byte one is a character rather than\n # its first byte three times.\n def center(width : Int32, pad : Char = ' ') : String\n def center(width : Int32, pad : String) : String\n def chars : Array(Char)\n # `\"\\n\"` is every line ending, as `chomp` with no argument reads it and as\n # the other library answers: `\"a\\r\\n\".chomp(\"\\n\")` is `\"a\"`.\n def chomp(suffix : String) : String\n def chomp : String\n def chop : String\n def count(char : Char) : Int32\n # The characters in every one of *sets*, a set read as the other\n # library's `Char#in_set?` reads one: `\"hello world\".count(\"a-z\")` is 10\n # and `\"hello\".count(\"^l\")` is 3. This counted *sets* as a substring, so\n # `\"banana\".count(\"an\")` was 2 here and 5 there, and a range or a `^`\n # was text: `count(\"a-z\")` was 0, and `delete` and `squeeze` took the\n # set as a list of the characters written.\n def count(*sets : String) : Int32\n def delete(char : Char) : String\n # The characters in every one of *sets*, as `count` reads them. Characters,\n # not bytes: a 256-entry byte mask cannot hold a character above ASCII,\n # and deleting one byte of one leaves the rest behind.\n def delete(*sets : String) : String\n def each_byte(& : (UInt8 -> Nil)) : Nil\n # Scanned, a line at a time: `lines` first built the whole array - 2.4\n # million strings for a 143 MB text - and `chomp` a second string for each.\n # The same lines, a final newline closing the last rather than opening an\n # empty one, and a `\\r` before a newline left off.\n def each_line(& : (String -> Nil)) : Nil\n def ends_with?(char : Char) : Bool\n # An empty search matches before every character and at the end, as\n # `sub` matches it at the start: `\"abc\".gsub(\"\", \"-\")` is `\"-a-b-c-\"`, the\n # other library's answer, where it was `\"abc\"`. A byte that begins no\n # character is kept, a unit of its own.\n def gsub(search : String, replacement : String) : String\n def gsub(search : Char, replacement : Char) : String\n def gsub(search : Char, replacement : String) : String\n def gsub(search : String, & : (String -> String)) : String\n def gsub(search : Char, & : (Char -> String)) : String\n def index(char : Char, offset : Int32) : Int32 | ::Nil\n def index(search : String, offset : Int32 = 0) : Int32 | ::Nil\n def self.join(items : Array(String), separator : String = \"\") : String\n # Into one builder, as `Enumerable#join` is and for its reason.\n def self.join(items, separator : String = \"\") : String\n # A `Char` above ASCII is more than one byte, and this family narrowed it\n # to one with a checked convert: `\"a☃b\".index('☃', 0)` panicked with\n # \"arithmetic overflow\", and so did nine of its siblings. Each asks the\n # character's own bytes now, which is what the string holds.\n def lstrip(char : Char) : String\n # Characters of *chars*, not bytes: a byte mask built from `\"©\"` (C2 A9)\n # took the last byte of `é` (C3 A9) with it and answered a lone C3, which\n # is not a string. ASCII is the mask; a wider character is looked up whole.\n def lstrip(chars : String) : String\n def lstrip : String\n def partition(separator : String) : Tuple(String, String, String)\n def partition(separator : Char) : Tuple(String, String, String)\n def presence : String | ::Nil\n # The characters in reverse, found from the back by `each_char`'s rule,\n # so each character's bytes are copied as they stand and a byte that\n # begins no character is one of its own: taking every `10xxxxxx` byte\n # with the lead before it kept `C0 80` and a stray byte after `é` whole.\n # It decoded the whole string into an array of `Char`s first - four\n # bytes each, 520 MB to reverse 130 MB of ASCII.\n def reverse : String\n def rindex(char : Char, offset : Int32) : Int32 | ::Nil\n def rindex(search : String, offset : Int32) : Int32 | ::Nil\n def rindex(char : Char) : Int32 | ::Nil\n def rindex(search : String) : Int32 | ::Nil\n def rpartition(separator : String) : Tuple(String, String, String)\n def rpartition(separator : Char) : Tuple(String, String, String)\n def rstrip(char : Char) : String\n def rstrip(chars : String) : String\n def rstrip : String\n def split(separator : Char, limit : Int32 | ::Nil) : Array(String)\n # No limit is `nil`, and one of 1 or less is the string whole, as the\n # other library answers both. A negative limit split everything, and 0\n # stood for none: `\"a,b\".split(\",\", -1)` was `[\"a\", \"b\"]` here and\n # `[\"a,b\"]` there. An empty string is one empty piece whatever the\n # limit, as it is without one: `\"\".split(\",\", -1)` was `[]`.\n def split(separator : String, limit : Int32 | ::Nil = nil) : Array(String)\n def split(limit : Int32 | ::Nil) : Array(String)\n def split : Array(String)\n def squeeze(char : Char) : String\n # A run of one character becomes one of it, a character being what\n # `each_char` yields, so a run of bytes that begin none is one U+FFFD, as\n # in the other library; grouped by their `10xxxxxx` bytes, C3 41 came back\n # as it was. A byte at a time, `\"耀\".squeeze` (E8 80 80) dropped a\n # continuation byte and answered two bytes that are not a string, and\n # `\"éé\"` stayed two.\n def squeeze : String\n # A run of the same character in every one of *sets* becomes one of it.\n # Compared per character, so a multi-byte one is a run rather than three\n # bytes that never match.\n def squeeze(*sets : String) : String\n def strip(char : Char) : String\n def strip(chars : String) : String\n def sub(search : String, replacement : String) : String\n def sub(search : Char, replacement : Char) : String\n def sub(search : Char, replacement : String) : String\n def sub(search : String, & : (String -> String)) : String\n # The character's own bytes, as the non-block form asks: narrowed to one\n # byte, `'☃'` panicked with \"arithmetic overflow\" and `'é'` matched nothing.\n def sub(search : Char, & : (Char -> String)) : String\n # The other library's reading, whitespace around it aside: a sign, then\n # `inf`, `infinity` or `nan` in any case (a `nan` may close with a `(...)`\n # of letters, digits and `_`), or decimal digits. Text whose value leaves\n # the range is nil, as that reader's ERANGE makes it: nonzero digits that\n # round to zero, or digits that round past the largest double. Here\n # `\"1e400\".to_f?` was Infinity, `\"1e-400\".to_f?` 0.0, and only `Infinity`\n # and `NaN` spelled so were words: `\"inf\".to_f?` was nil.\n def to_f? : Float64 | ::Nil\n def to_i(base : Int32) : Int32\n def to_i64(base : Int32) : Int64\n def to_i64 : Int64\n def to_i64?(base : Int32) : Int64 | ::Nil\n def to_i64? : Int64 | ::Nil\n # Whitespace before and after the number is not part of it, as the other\n # library reads one (`Char#whitespace?`, so a no-break space too); inside\n # it is still refused. `\" 42\".to_i64?`, `\"ff \".to_i?(16)` and a line\n # read with its `\\n` were nil here, and 42 and 255 there.\n def to_i?(base : Int32) : Int32 | ::Nil\n def to_u64(base : Int32) : UInt64\n # The unsigned 64-bit reading, for the checksums and hashes a port keeps\n # in `UInt64`: digits in *base*, an optional `+`, and nil for a sign,\n # anything that is not a digit, or a value past 18446744073709551615.\n def to_u64 : UInt64\n def to_u64?(base : Int32) : UInt64 | ::Nil\n def to_u64? : UInt64 | ::Nil\n # Mapped per character. The 256-entry byte table this used could not hold\n # a character above ASCII: `\"a☃\".tr(\"☃\", \"x\")` answered `\"axxx\"`, one `x`\n # per byte of the character it was asked to replace once. Both sets name\n # runs: `a-z` is the twenty-six letters, and a `-` at either end is\n # itself, as the other library's `delete` sets read them. Its `tr` pairs\n # the characters positionally, a `-` among them, so\n # `\"bab\".tr(\"a-c\", \"A-C\")` is `\"bAb\"` there and `\"BAB\"` here.\n #\n # Both sets in ASCII - the common case, and a DNA complement's - are one\n # 256-entry byte table built once and one pass over the bytes, which a\n # character above ASCII passes through untouched because none of its\n # bytes is under 0x80. A byte that begins no character is not one of\n # those: the table kept it as it stood where the per-character map\n # writes U+FFFD, so text holding one takes that map too. Otherwise the\n # map is per character. A `to` shorter than `from` repeats its last\n # character, which is the rule the other library follows; an empty one\n # deletes the character. Each character was looked up in an array of\n # one-character strings and the answer appended to a string rebuilt every\n # time, and a 130 MB `tr` did not finish.\n def tr(from : String, to : String) : String\nend\n" }, { "name": "std/time", "path": "src/std/time.iyi", - "sourceSha256": "5282622222a57554e3e1f9379004f3fdb2194b89ef3c8829a804b95da1091ac3", - "surface": "module std/time\n\npub struct DayOfWeek\n def ==(other : DayOfWeek) : Bool\n def ===(other : DayOfWeek) : Bool\n def friday? : Bool\n # Hashing follows `==`. Without it a day hashed to its type, `Object#hash`'s\n # default: `Monday.hash == Sunday.hash`, and every day a table held went to\n # one slot.\n def hash : Int32\n def initialize(value : Int32)\n def monday? : Bool\n def saturday? : Bool\n def sunday? : Bool\n def thursday? : Bool\n def to_s : String\n def tuesday? : Bool\n def value : Int32\n def wednesday? : Bool\nend\n\n# A time zone: the offsets from UTC a place has kept, and when it changed\n# from one to the next. `Location.load(\"Europe/Istanbul\")` reads the IANA\n# database the machine has; `Location.local` is the machine's own zone;\n# `Location.fixed(19800)` is an offset and nothing else; `Location::UTC` is\n# UTC. A `Time` carries one, and its date and clock are read in it.\n#\n# The database is the system's TZif files, RFC 8536: `$ZONEINFO` when it\n# names a directory, then `/usr/share/zoneinfo`, `/usr/share/lib/zoneinfo`\n# and `/etc/zoneinfo`. Versions 1 to 4 are read. A time before the first\n# change has the file's first offset, and one after the last has its POSIX\n# footer's rule, so a slim file answers for the year 2100 as a fat one does.\n# Leap seconds are not counted, as no clock this module reads counts them.\n#\n# `Location.local` is the `TZ` variable when it is set — the empty string\n# is UTC, a leading `:` is dropped, an absolute path is a TZif file, a name\n# is looked up in the database, and anything else is read as a POSIX TZ\n# string such as `EST5EDT,M3.2.0,M11.1.0` — and `/etc/localtime` when it is\n# not. Windows has no database to read: there `local` is the system's zone,\n# its rule from `GetTimeZoneInformation`, its zones named by their offsets,\n# and `load` reads `$ZONEINFO` alone. Where nothing answers, local is UTC.\n# The local zone is read once, the first time it is asked for.\n#\n# A location is built once and never changed, so a `Time` that holds one\n# can cross to another task.\npub class Location\n # One offset a location keeps: its abbreviation (`CEST`, `+03`), its\n # seconds east of UTC, and whether it is summer time.\n struct Zone\n def ==(other : Zone) : Bool\n def dst? : Bool\n def initialize(name : String, offset : Int32, dst : Bool)\n def name : String\n def offset : Int32\n def to_s : String\n end\n def ==(other : Location) : Bool\n def self.fixed(name : String, offset : Int32) : Location\n # An offset of *offset* seconds east of UTC and nothing else, named as\n # the offset is written: `+05:30`. Less than a day either way.\n def self.fixed(offset : Int32) : Location\n # One offset, never changing.\n def fixed? : Bool\n def hash : Int32\n def initialize(name : String, zones : Array(Zone), transitions : Array(Int64), transition_zones : Array(Int32), rule : TzRule | ::Nil)\n def inspect : String\n # The zone the IANA database names *name*: `Europe/Istanbul`,\n # `America/New_York`. `UTC` and the empty name are UTC and `Local` is\n # `Location.local`, without a file. A name that climbs out of the database\n # (`..`, a leading `/`, a `\\`) is refused, and so is one no directory has.\n def self.load(name : String) : Location | LocationError\n # The machine's zone; see the type's comment for where it comes from.\n def self.local : Location\n # The machine's own zone, the one `Location.local` answers.\n def local? : Bool\n # The zone *time* is in here.\n def lookup(time : Time) : Zone\n def lookup(unix_seconds : Int64) : Zone\n def name : String\n # A location from the 172 bytes of a Windows TIME_ZONE_INFORMATION, which\n # is what `Location.local` reads on Windows; it is pure so that every\n # platform can check the decoding with bytes made by hand. The struct is\n # the bias in minutes west of UTC (UTC = local + bias), then the standard\n # name, date and bias, then the daylight ones, all little-endian. The\n # three biases are signed LONGs: a zone east of UTC has a negative Bias\n # (Turkey's is -180) and a summer usually a DaylightBias of -60, so each\n # is read as the 32 bits' two's complement. Read as unsigned and then\n # converted, as it once was, -180 was 4294967116 and the conversion\n # panicked with an arithmetic overflow, issue #133.\n #\n # A date with a year of 0 is a rule - the wDay'th wDayOfWeek of wMonth,\n # 5 the last, at the wall time then in force - which is a POSIX\n # `Mm.w.d/time`; one with a month of 0 is no summer. A date with a year\n # is a single year's, which is not a rule at all, and it is read as no\n # summer rather than as every year's. The names are the offset's, `+03`\n # or `-05`, as tzdata writes a zone it has no letters for: the struct's\n # own are Windows' long names, \"Turkey Standard Time\", which are not\n # abbreviations and are localised.\n def self.read_windows_zone(name : String, data : String) : Location | LocationError\n # A location from the bytes of a TZif file, RFC 8536, named *name*.\n # Anything that is not one is refused with what was wrong and where.\n def self.read_zoneinfo(name : String, data : String) : Location | LocationError\n # The instant a wall clock here reads *wall* (seconds since 1970 as a\n # UTC clock would count them). A wall time a change repeats is the\n # earlier of the two instants; one a change skips is read on the clock\n # from before the change, which lands it past the gap: 02:30 on the\n # morning summer starts in Berlin is 03:30 CEST. Python's `fold=0` and\n # Go read them the same way. `Time.local` and a parse at a location ask\n # this.\n def resolve(wall : Int64) : Int64\n def to_s : String\n def utc? : Bool\nend\n\n# What `Location.load` and `Location.read_zoneinfo` answer when there is no\n# zone by that name, or the bytes are not a zone: a name the program was\n# handed or a file on the machine is not a bug in the program, so it is a\n# value and not a panic.\npub struct LocationError\n def initialize(message : String)\n def to_s : String\nend\n\npub struct Span\n def +(other : Span) : Span\n def -(other : Span) : Span\n def - : Span\n def <(other : Span) : Bool\n def <=(other : Span) : Bool\n def <=>(other : Span) : Int32\n def ==(other : Span) : Bool\n def >(other : Span) : Bool\n def >=(other : Span) : Bool\n # The seconds are an Int64, and a span that leaves it is refused here,\n # by name: `Span.seconds(Int64::MAX) + Span.seconds(1)` panicked with the\n # prelude's \"arithmetic overflow\", a sentence that named no span. The\n # wrapping sum is compared with an addend to see whether it wrapped; the\n # product is bounded before it is taken.\n def self.add_seconds(a : Int64, b : Int64) : Int64\n def days : Int64\n def self.days(n : Int64) : Span\n # Hashing follows `==`, the two parts mixed as a tuple's members are. Without\n # it every span hashed to its type: `Span.seconds(1).hash ==\n # Span.days(9).hash`, and a table of 16,000 spans took 0.95 s to fill where\n # the same pairs as tuples took 1 ms.\n def hash : Int32\n def hours : Int32\n def self.hours(n : Int64) : Span\n def initialize(seconds : Int64 = 0_i64, nanoseconds : Int32 = 0)\n def millisecond : Int32\n def milliseconds : Int32\n def self.milliseconds(n : Int64) : Span\n def minutes : Int32\n def self.minutes(n : Int64) : Span\n def self.mul_seconds(n : Int64, unit : Int64) : Int64\n def nanosecond : Int32\n def nanoseconds : Int32\n def self.nanoseconds(n : Int64) : Span\n def self.new(*, days : Int64 = 0_i64, hours : Int64 = 0_i64, minutes : Int64 = 0_i64, seconds : Int64 = 0_i64, milliseconds : Int64 = 0_i64, nanoseconds : Int64 = 0_i64) : Span\n def seconds : Int32\n def self.seconds(n : Int64) : Span\n def self.sub_seconds(a : Int64, b : Int64) : Int64\n def to_i : Int64\n def to_s : String\n def total_days : Float64\n def total_hours : Float64\n def total_milliseconds : Float64\n def total_minutes : Float64\n def total_nanoseconds : Float64\n def total_seconds : Float64\n def total_seconds_i64 : Int64\n def self.zero : Span\nend\n\npub struct Time\n alias DayOfWeek = Std::Time::DayOfWeek\n # A pattern of Crystal's directives, for writing a `Time` and reading one\n # back. Text that is not a directive is written as it is and must be\n # there, byte for byte, when reading; ASCII whitespace in the pattern is\n # written as it is and reads any run of whitespace, none included.\n #\n # %Y year, four digits or more, year -1 as -0001 %C year // 100\n # %y year % 100 %G %g %V the ISO 8601 week year, its last two digits,\n # and the week 01..53 %m month 01..12 %B January %b %h Jan\n # %d day 01..31 %e day \" 1\"..\"31\" %j day of the year 001..366\n # %H hour 00..23 %k \" 0\"..\"23\" %I 01..12 %l \" 1\"..\"12\" %p am %P AM\n # %M minute %S second %L milliseconds %3N %6N %9N the fraction to\n # 3, 6 and 9 digits %N nanoseconds, any number of digits when read\n # %A Sunday %a Sun %u day of the week, Monday 1..7 %w Sunday 0..6\n # %s seconds since the epoch %z +0530 %:z +05:30 %::z +05:30:00\n # %Z and %^Z: UTC at offset 0 and the offset, +05:30, at any other\n # %F %Y-%m-%d %T %X %H:%M:%S %R %H:%M %D %x %m/%d/%y\n # %r %I:%M:%S %P %c %a %b %e %H:%M:%S %Y %n newline %t tab %% %\n #\n # A number takes a flag between the `%` and its letter: `-` for no\n # padding (`%-d` is 5), `_` for spaces (`%_m` is \" 5\") and `0` for zeros\n # (`%0e` is 05); `^` upcases a name (`%^a` is SUN). The other library knows\n # `%-d`, `%-m`, `%_m` and the `^` names. The same flags on its other numbers\n # are this module's, where that library wrote the `%-` back out as text; a `%`\n # that starts no directive is text here as it is there.\n #\n # Reading takes up to a number's width in digits, fewer when the next byte\n # is not a digit, and a name in any case. `%Y` takes four digits when a\n # directive follows it (`%Y%m%d`), and when text or the end of the\n # pattern does (`%F`), an optional `-` and up to ten, so the years `to_s`\n # writes are the years it reads. A field outside its range, a day the\n # month does not have, a name that is not one and text after the pattern\n # are each a `TimeParseError`, never a panic. A day name is checked to be\n # one and not checked against the date, as Crystal, Go and Python read it.\n struct Format\n # The parts of an instant as they read at an offset, worked out once\n # per `format` rather than once per directive. *zone* is what `%Z`\n # writes, the location's abbreviation; with none it writes `UTC` at\n # offset 0 and the offset at any other.\n struct Fields\n def day : Int32\n def day_of_week : Int32\n def day_of_year : Int32\n def hour : Int32\n def initialize(time : Time, offset : Int32, zone : String | ::Nil)\n def minute : Int32\n def month : Int32\n def nanosecond : Int32\n def offset : Int32\n def second : Int32\n def unix : Int64\n def week : ::Tuple(Int64, Int32)\n def year : Int32\n def zone : String | ::Nil\n end\n # The HTTP date of RFC 9110 section 5.6.7. It is written as the\n # IMF-fixdate, always in GMT, `Sun, 06 Nov 1994 08:49:37 GMT`; it is\n # read in all three of the forms a recipient must accept: that one, the\n # obsolete RFC 850 `Sunday, 06-Nov-94 08:49:37 GMT`, and asctime's\n # `Sun Nov 6 08:49:37 1994`.\n module HTTP_DATE\n def self.format(time : Time) : String\n def self.parse(string : String) : Time | TimeParseError\n end\n # ISO 8601's calendar date, written `2016-02-15` as the date reads at\n # *offset* or in the time's own location; read extended or basic, and\n # as an ordinal or a week date, at *offset* since a date names no zone.\n module ISO_8601_DATE\n def self.format(time : Time, offset : Int32 | ::Nil = nil) : String\n def self.parse(string : String, offset : Int32 = 0) : Time | TimeParseError\n end\n # ISO 8601's date and time, written as RFC 3339 writes it and read with\n # every form of `ISO_8601_DATE` and `ISO_8601_TIME` joined by an\n # optional `T`. The zone is required, as Crystal requires it.\n module ISO_8601_DATE_TIME\n def self.format(time : Time, offset : Int32 | ::Nil = nil) : String\n def self.parse(string : String) : Time | TimeParseError\n end\n # ISO 8601's time of day and its zone, `04:35:50Z`; read as on\n # 0001-01-01, the day Crystal reads it on.\n module ISO_8601_TIME\n def self.format(time : Time, offset : Int32 | ::Nil = nil) : String\n def self.parse(string : String) : Time | TimeParseError\n end\n # Reads a text by a pattern or by one of the named grammars. Every step\n # answers whether it matched; the first that did not leaves its sentence\n # in `@error`, and `finish` answers that, so no text a program is handed\n # ends it in a panic.\n class Parser\n def accept(char : Char) : Bool\n def am_pm : Bool\n def at_end : Bool\n # RFC 5322's CFWS: whitespace and parenthesised comments, which nest\n # and in which `\\` quotes the next byte. Answers whether there was any;\n # a comment left open is the error, and the parse fails on it.\n def cfws : Bool\n def day_name : Bool\n def self.digit?(b : UInt8) : Bool\n # Up to *max* digits and at least one. Nil, with the sentence, when\n # there are none, or when they are past what an Int64 holds.\n def digits(max : Int32, what : String) : Int64 | ::Nil\n def directive(letter : Char, flag : Char, digits : Int32, wide : Bool) : Bool\n # Exactly *count* digits, or nil, reading nothing and saying nothing:\n # the caller names what was missing.\n def exact(count : Int32) : Int32 | ::Nil\n def expect(char : Char) : Bool\n def fail(what : String) : Bool\n # A numeric directive's field, read as its flag and its padding say.\n def field(width : Int32, pad : Char, flag : Char, what : String) : Int32 | ::Nil\n # The instant the fields name, or the first sentence a step left. The\n # fields are checked the way `Time.utc` checks them, with RFC 3339's\n # allowance: `:60` is a leap second, read as the second after `:59`,\n # the instant it names on every clock that has none. A text that\n # names no zone is read at *default_offset*, or failing that on\n # *location*'s wall clock; with a location the time answered is in it.\n def finish(default_offset : Int32 | ::Nil, location : Location | ::Nil = nil) : Time | TimeParseError\n # A fraction of a second: up to *max* digits, scaled to nanoseconds,\n # and for `%N` any digits past the ninth read and dropped.\n def fraction(max : Int32, rest : Bool) : Bool\n # RFC 9110 section 5.6.7's three forms of an HTTP date: the\n # IMF-fixdate `Sun, 06 Nov 1994 08:49:37 GMT`, the obsolete RFC 850\n # `Sunday, 06-Nov-94 08:49:37 GMT`, and asctime's `Sun Nov 6 08:49:37\n # 1994`, which has no zone and is GMT. Crystal's reading: a day name,\n # short or long, in any of the three; the four-digit year IIS writes\n # in the RFC 850 shape; and the zones RFC 2822 names where RFC 9110\n # writes GMT.\n def http_date : Bool\n def initialize(input : String)\n # ISO 8601's dates, extended or basic: 2016-02-15 and 20160215, the\n # ordinal 2016-046 and 2016046, and the week date 2016-W07-1 and\n # 2016W071.\n def iso_date : Bool\n def iso_date_time : Bool\n # ISO 8601's time of day, extended or basic: 04:35:50 and 043550, a\n # fraction of the second after `.` or `,`, or the minutes or the hour\n # alone with a fraction of their own (04:35.5 is 04:35:30).\n def iso_time : Bool\n def self.letter?(b : UInt8) : Bool\n def literal(b : UInt8) : Bool\n def month_name(short : Bool) : Bool\n # A run of ASCII letters and the index in *names* of the name it\n # spells, in any case: for *short*, the first three letters of a name\n # and no more; otherwise the start of a name, three letters or longer,\n # as Crystal reads `%B` and `%A`.\n def name(names : Array(String), short : Bool, what : String) : Int32 | ::Nil\n def need_cfws : Bool\n def number(max : Int32, what : String) : Int32 | ::Nil\n def offset_number(allow_colon : Bool, force_colon : Bool, allow_seconds : Bool) : Bool\n # A number blank padded, as `%e` writes \" 5\": one space stands for a\n # digit.\n def padded_number(max : Int32, what : String) : Int32 | ::Nil\n def pattern(pattern : String) : Bool\n # The byte *ahead* of the current one, or 0 past the end, as Crystal's\n # reader answers '\\0' there.\n def peek(ahead : Int32 = 0) : UInt8\n # RFC 9110's rule for a two-digit year (section 5.6.7): a year that\n # appears to be more than 50 years in the future is the most recent\n # past year with those last two digits. The candidate is taken in this\n # century and moved a century to land in the 100 years ending 50 years\n # from now, so the rule holds in 2090 too. The other library read 00..49 as 20xx\n # and the rest as 19xx, whatever the year it was read in.\n def self.recent_year(two_digits : Int64) : Int64\n # A sentence about fields that were all read, where no one byte is to\n # blame.\n def refuse(what : String) : TimeParseError\n # RFC 5322 section 3.3's date-time, with section 4.3's obsolete\n # two- and three-digit years and zone names, as Crystal's `RFC_2822`\n # reads it: `Mon, 15 Feb 2016 04:35:50 +0000`, the day name and the\n # seconds optional, comments anywhere whitespace goes.\n def rfc_2822 : Bool\n def same_letters?(start : Int32, name : String, size : Int32) : Bool\n def skip_spaces : Bool\n def self.space?(b : UInt8) : Bool\n def text(start : Int32, stop : Int32) : String\n # HH:MM:SS, as both HTTP and RFC 850 write the time of day.\n def time_of_day : Bool\n # One whitespace byte, as the HTTP grammars put between their parts.\n def whitespace : Bool\n # `%Y`: four digits when a directive follows it, and an optional `-`\n # and up to ten when text does, which is every year an Int32 holds.\n def year(wide : Bool) : Int64 | ::Nil\n # Exactly four digits, as the HTTP and ISO 8601 grammars write a year.\n def year4 : Bool\n # `%z`, `%:z` and `%::z` alike, as Crystal reads them: `Z`, `UTC`, or\n # an offset with or without its colons.\n def zone : Bool\n # `%Z`: an offset, or a name read up to the next whitespace. Only the\n # names of UTC itself are read: an abbreviation names no single zone\n # (CST is three of them), so any other is refused by name rather than\n # read as UTC or guessed at. A text whose zone is known is read at\n # that `Location` instead.\n def zone_name : Bool\n # RFC 5322's zone (section 3.3, and 4.3 for the obsolete names): an\n # offset of four digits, or a name. A military letter is read as\n # `-0000`, the offset of a time whose zone is not known, as section\n # 4.3 says to; a name the RFC does not give is refused, where Crystal\n # read it as UTC in silence. `UTC` is not the RFC's, and is read as\n # Crystal reads it.\n def zone_rfc2822 : Bool\n # ISO 8601's zone: `Z` or an offset.\n def zone_z_or_offset : Bool\n end\n # RFC 2822's date and time, `Mon, 15 Feb 2016 04:35:50 +0000`, as\n # Crystal writes it: the day of the month unpadded, the offset at the\n # end, the time's own unless another is given. RFC 2822 writes an offset\n # in whole minutes, so an offset with seconds is refused rather than\n # written as some other instant.\n module RFC_2822\n def self.format(time : Time, offset : Int32 | ::Nil = nil) : String\n def self.parse(string : String) : Time | TimeParseError\n end\n # RFC 3339, the module's own serialisation: `to_rfc3339` and\n # `parse_rfc3339` are this module's `format` and `parse`, and\n # `parse_rfc3339` panics with the sentence `parse` answers. Written at\n # *offset*, or at the time's own when it is whole minutes and in UTC\n # when it is not.\n module RFC_3339\n def self.format(time : Time, fraction_digits : Int32 = 0, offset : Int32 | ::Nil = nil) : String\n def self.parse(str : String) : Time | TimeParseError\n end\n # An offset is less than a day either side of UTC: the widest RFC 3339\n # and RFC 2822 write is 23:59, and a day or more is a date, not a zone.\n # An offset is the program's own, so a wrong one is a panic.\n def self.check_offset(offset : Int32) : Nil\n def format(time : Time, offset : Int32) : String\n def format(time : Time) : String\n # *offset*, seconds east of UTC, is the one a parse reads a text at when\n # the text names no zone, and the one `format` writes at. Nil, as for\n # Crystal's `Format.new(pattern)`, makes a text without a zone an error,\n # and `format` writes a time in its own location.\n def initialize(pattern : String, offset : Int32 | ::Nil = nil)\n # A text that names no zone is read on *location*'s wall clock, and\n # every time is read and written in *location*.\n def initialize(pattern : String, location : Location)\n # The directive at the `%` at byte *i* of *pattern*: the byte after it,\n # its letter, its flag, the colons of `%:z` and `%::z`, and the digits of\n # `%3N`. A `%` that starts no directive answers the letter '\\0' and the\n # byte after the `%`: it is text, and so is what follows it, as Crystal\n # writes `%Q` back out as `%Q`.\n def self.lex(pattern : String, i : Int32) : ::Tuple(Int32, Char, Char, Int32, Int32)\n def location : Location | ::Nil\n def self.name(io : String::Builder, text : String, flag : Char) : Nil\n # A number padded to *width* with *pad*, '\\0' for none, unless a flag\n # says otherwise. The sign of a negative year goes in front of the\n # padding, as `-0001`, the way `to_rfc3339` writes it.\n def self.number(io : String::Builder, value : Int64, width : Int32, pad : Char, flag : Char) : Nil\n def self.numeric?(letter : Char) : Bool\n def offset : Int32 | ::Nil\n # `+hh:mm` or `+hhmm`, and the seconds never (0), when there are any (1)\n # or always (2), the way Crystal's `Zone#format` writes an offset.\n def self.offset_text(offset : Int32, colon : Bool, seconds : Int32) : String\n def parse(string : String, offset : Int32 | ::Nil) : Time | TimeParseError\n def parse(string : String) : Time | TimeParseError\n def pattern : String\n def self.plain?(letter : Char) : Bool\n def self.textual?(letter : Char) : Bool\n def self.twelve(hour : Int32) : Int64\n def self.write(io : String::Builder, pattern : String, f : Fields) : Nil\n def self.write_directive(io : String::Builder, letter : Char, flag : Char, colons : Int32, digits : Int32, f : Fields) : Nil\n end\n struct KernelTimeSpec\n def initialize(tv_sec : Int64, tv_nsec : Int64)\n def tv_nsec : Int64\n def tv_nsec=(value : Int64) : Int64\n def tv_sec : Int64\n def tv_sec=(value : Int64) : Int64\n end\n alias Location = Std::Time::Location\n alias Span = Std::Time::Span\n # The sum is checked before it is taken, so a span too long for the\n # seconds is refused as one; a sum that fits but leaves the years an\n # Int32 names is refused by the constructor. A span is exact: a day\n # added across the night summer begins is 24 hours, and the clock reads\n # an hour later than it did.\n def +(span : Span) : Time\n def -(span : Span) : Time\n def -(other : Time) : Span\n def <(other : Time) : Bool\n def <=(other : Time) : Bool\n def <=>(other : Time) : Int32\n def ==(other : Time) : Bool\n def >(other : Time) : Bool\n def >=(other : Time) : Bool\n # The ISO 8601 week-numbering year and week this day falls in, as\n # Crystal's `calendar_week` answers: 2021-01-01 is in week 53 of 2020.\n def calendar_week : ::Tuple(Int32, Int32)\n # Converts days since 1970-01-01 into {year, month, day}.\n def self.civil_from_days(z : Int64) : ::Tuple(Int32, Int32, Int32)\n def day : Int32\n def day_of_week : DayOfWeek\n def day_of_year : Int32\n def self.days_from_civil(y : Int64, m : Int32, d : Int32) : Int64\n def self.days_in_month(year : Int32, month : Int32) : Int32\n def self.days_in_year(year : Int32) : Int32\n # Number of days between 1970-01-01 and the given civil date.\n # Follows Howard Hinnant's Gregorian calendar algorithm.\n def self.floor_div(a : Int64, b : Int64) : Int64\n def self.floor_mod(a : Int64, b : Int64) : Int64\n def self.from_unix(seconds : Int64, nanoseconds : Int32 = 0) : Time\n def self.from_unix_ms(milliseconds : Int64) : Time\n # Hashing follows `==`, as `Span`'s does. Without it every time hashed to its\n # type: `Time.unix(1).hash == Time.unix(1000000, 5).hash`, and a table of\n # 16,000 times took 1.2 s to fill where the same pairs as tuples took 1 ms.\n def hash : Int32\n def hour : Int32\n # The same instant read in *location*: `Time.utc(2024, 7, 1, 12).in(berlin)`\n # is 14:00 CEST.\n def in(location : Location) : Time\n def initialize(seconds : Int64, nanoseconds : Int32 = 0, location : Location = Location::UTC)\n # As the other library writes it: the date and time, the nanoseconds\n # when there are any, and `Z`. There was no `inspect`, so it was `to_s`,\n # and `Time.utc(2024, 1, 1, 0, 0, 0, nanosecond: 5)` inspected as\n # `2024-01-01 00:00:00 UTC`, the same as `Time.utc(2024, 1, 1)`, which\n # `==` tells apart from it. Out of UTC the offset follows, and the\n # location's name when it is more than an offset:\n # `2016-02-15 11:20:30 +01:00 Europe/Berlin`.\n def inspect : String\n # The ISO 8601 week-numbering year and week of a date, from its year, day\n # of the month, day of the year and day of the week (Monday is 1). This is\n # Crystal's computation: the week is the count of Mondays up to the day,\n # and the first days of January or the last of December can belong to a\n # week of the year next to it. The year is an Int64 because the first days\n # of the smallest Int32 year belong to the year before it.\n def self.iso_week(year : Int64, day : Int32, day_of_year : Int32, day_of_week : Int32) : ::Tuple(Int64, Int32)\n def self.kernel_clock_gettime(clock_id : Int64) : ::Tuple(Int64, Int64)\n def self.leap_year?(year : Int32) : Bool\n # The time a clock in *location* shows as the date and time given, the\n # machine's own zone when none is. Its fields are checked as `utc` checks\n # them. A wall time a change of offset repeats, the hour after summer\n # ends, is the earlier of the two; one a change skips, the hour summer\n # begins with, is read past the gap (see `Location#resolve`).\n def self.local(year : Int32, month : Int32, day : Int32, hour : Int32 = 0, minute : Int32 = 0, second : Int32 = 0, millisecond : Int32 = 0, nanosecond : Int32 = 0, location : Location = Location.local) : Time\n # Now, read in *location*.\n def self.local(location : Location) : Time\n # Now, in the machine's own zone (`Location.local`).\n def self.local : Time\n def local? : Bool\n # The zone the date and clock are read in, and its offset from UTC at\n # this instant in seconds east. `==`, `<=>` and `hash` are the instant's\n # and do not look at either: noon in London and 13:00 in Paris are the\n # same time.\n def location : Location\n def millisecond : Int32\n def milliseconds : Int32\n def minute : Int32\n def self.monotonic : Span\n def self.monotonic_nanoseconds : UInt64\n def month : Int32\n def nanosecond : Int32\n def nanoseconds : Int32\n def self.new(year : Int32, month : Int32, day : Int32, hour : Int32 = 0, minute : Int32 = 0, second : Int32 = 0, millisecond : Int32 = 0, nanosecond : Int32 = 0) : Time\n def offset : Int32\n def self.pad2(n : Int32) : String\n def self.pad3(n : Int32) : String\n # Four digits, and a negative year keeps its sign in front of them rather\n # than inside: written as `n < 10 ? \"000\" + n.to_s`, year -1 printed\n # `000-1-01-01`, which is neither a year nor a date. ISO 8601 spells it\n # `-0001`, and the conversions themselves were right all along - the epoch\n # round-trips through it.\n def self.pad4(n : Int32) : String\n def self.pad6(n : Int32) : String\n def self.pad9(n : Int32) : String\n # Reads *string* by *pattern*'s directives (see `Time::Format`). A text\n # that names no zone is read at *offset* seconds east of UTC, as Crystal's\n # `Time.parse` reads one at the location it is given.\n def self.parse(string : String, pattern : String, offset : Int32) : Time | TimeParseError\n # Reads *string* by *pattern* in *location*: a text that names no zone is\n # the wall clock there, its offset the one the location kept at that date\n # (a wall time a change repeats or skips is read as `Time.local` reads\n # it), and the time answered is in *location* whether the text named a\n # zone or not.\n def self.parse(string : String, pattern : String, location : Location) : Time | TimeParseError\n def self.parse_iso8601(string : String) : Time | TimeParseError\n # `parse` in the machine's own zone.\n def self.parse_local(string : String, pattern : String) : Time | TimeParseError\n # Any of RFC 9110's three HTTP date forms; see `Time::Format::HTTP_DATE`.\n def self.parse_rfc1123(string : String) : Time | TimeParseError\n def self.parse_rfc2822(string : String) : Time | TimeParseError\n # Parses an RFC 3339 / ISO 8601 string into a UTC Time, and panics with the\n # parser's sentence when it is not one. Handles forms like:\n # 2024-02-29T12:30:45Z\n # 2024-02-29T12:30:45.123Z\n # 2024-02-29T12:30:45.123456789Z\n # 2024-02-29 12:30:45Z\n # 2024-02-29T12:30:45+05:00\n # 2024-02-29T12:30:45-08:00\n # -0001-01-01T00:00:00Z and 12345-06-07T08:09:10Z, as `to_rfc3339` writes them\n # `Time::Format::RFC_3339.parse` reads the same grammar and answers a\n # `TimeParseError` instead, for text the program did not write itself.\n def self.parse_rfc3339(str : String) : Time\n # `parse` at UTC when the text names no zone.\n def self.parse_utc(string : String, pattern : String) : Time | TimeParseError\n def second : Int32\n def seconds : Int64\n # Moves the date by calendar years and months, then by weeks and days,\n # then by the smaller units, in that order, as Crystal's `shift` does. A\n # day the month does not have becomes its last: `Time.utc(2024, 1,\n # 31).shift(months: 1)` is 2024-02-29. The date moves on the wall clock\n # of the time's location, so the time of day is kept across a change of\n # offset - a day after 12:00 on the Saturday summer begins is 12:00 on\n # Sunday, 23 hours later - and the hours, minutes, seconds and\n # nanoseconds are exact. A year past an Int32 is refused with the\n # constructor's sentence, and a span the Int64 seconds do not hold with\n # `Span`'s.\n def shift(*, years : Int32 = 0, months : Int32 = 0, weeks : Int64 = 0_i64, days : Int64 = 0_i64, hours : Int64 = 0_i64, minutes : Int64 = 0_i64, seconds : Int64 = 0_i64, nanoseconds : Int64 = 0_i64) : Time\n def to_local : Time\n # RFC 1123's date as HTTP writes it, RFC 9110's IMF-fixdate, always in GMT:\n # `Sun, 06 Nov 1994 08:49:37 GMT`.\n def to_rfc1123 : String\n # RFC 2822's date, `Mon, 15 Feb 2016 04:35:50 +0000`, at *offset*, or at\n # the time's own offset when none is given.\n def to_rfc2822(offset : Int32 | ::Nil = nil) : String\n # RFC 3339 at the time's own offset: `Z` at UTC, `+02:00` in CEST. An\n # offset RFC 3339 cannot write, one with seconds in it as local mean time\n # before standard time had, is written as the same instant in UTC.\n def to_rfc3339(fraction_digits : Int32 = 0) : String\n # The instant written as it reads at *offset* seconds east of UTC:\n # `to_s(\"%F %T %:z\", 19800)` is the wall clock in India.\n def to_s(format : String, offset : Int32) : String\n # The time written by *format*'s directives (see `Time::Format`) as its\n # own location reads it; `%Z` is the zone's abbreviation, `CEST`.\n def to_s(format : String) : String\n # `2016-02-15 10:20:30 UTC`, and in any other location the offset in\n # place of `UTC`: `2016-02-15 11:20:30 +01:00`.\n def to_s : String\n def to_unix : Int64\n def to_unix_f : Float64\n # Refused with the module's sentence past the milliseconds an Int64\n # holds, some 292 million years either side of 1970, where the multiply\n # was the prelude's bare \"arithmetic overflow\" for a year `Time.utc`\n # accepts, 300,000,000. Both edges answer to the last millisecond that\n # fits: the lowest 808 an Int64 holds were refused, so\n # `Time.unix_ms(Int64::MIN).to_unix_ms` panicked. A negative second is\n # taken one up and its milliseconds from 1000 down, so the product fits\n # too.\n def to_unix_ms : Int64\n def to_utc : Time\n def self.unix(seconds : Int64, nanoseconds : Int32 = 0) : Time\n def self.unix_ms(milliseconds : Int64) : Time\n def self.utc(year : Int32, month : Int32, day : Int32, hour : Int32 = 0, minute : Int32 = 0, second : Int32 = 0, millisecond : Int32 = 0, nanosecond : Int32 = 0) : Time\n def self.utc : Time\n def utc? : Bool\n def self.wall_clock_nanoseconds : UInt64\n # The instant of a day of an ISO 8601 week, as Crystal's `week_date` makes\n # it: `Time.week_date(2016, 1, 1)` is Monday 2016-01-04. A week the year\n # does not have and a day past Sunday are refused, where the other library answered\n # a day of some other week.\n def self.week_date(year : Int32, week : Int32, day_of_week : Int32, hour : Int32 = 0, minute : Int32 = 0, second : Int32 = 0, nanosecond : Int32 = 0) : Time\n # Days since 1970-01-01 of a day of an ISO 8601 week. Week 1 is the week\n # holding January 4th, so its Monday is the Monday on or before that day.\n def self.week_date_days(year : Int32, week : Int32, day_of_week : Int32) : Int64\n # A year has 53 ISO 8601 weeks when it starts on a Thursday, or on a\n # Wednesday in a leap year: either way it has 53 Thursdays, and a week\n # belongs to the year that holds its Thursday.\n def self.weeks_in_year(year : Int32) : Int32\n def year : Int32\n # The zone this instant is in at its location: its abbreviation, offset\n # and whether it is summer time.\n def zone : Location::Zone\nend\n\n# What `Time::Format#parse` and the named parsers answer when the text is not\n# a time the pattern describes. Bad input is the caller's to handle, not a\n# panic: a header or a log line from somewhere else is not a bug in the\n# program reading it. `message` is the sentence, naming the byte the parser\n# stood at and the text; `position` is that byte, counted from 0.\npub struct TimeParseError\n def initialize(message : String, input : String, position : Int32)\n def input : String\n def position : Int32\n def to_s : String\nend\n\nimpl Error for TimeParseError\n def message : String\nend\n\nimpl Std::Traits::Comparable for Span\n def <=>(other : self) : Int32\nend\n\nimpl Error for LocationError\n def message : String\nend\n\nimpl Std::Traits::Comparable for Time\n def <=>(other : self) : Int32\nend\n" + "sourceSha256": "98656531b9416af7f2556cd367d2716d4b955b609a7ad2df35f48d8898d789e9", + "surface": "module std/time\n\npub struct DayOfWeek\n def ==(other : DayOfWeek) : Bool\n def ===(other : DayOfWeek) : Bool\n def friday? : Bool\n # Hashing follows `==`. Without it a day hashed to its type, `Object#hash`'s\n # default: `Monday.hash == Sunday.hash`, and every day a table held went to\n # one slot.\n def hash : Int32\n def initialize(value : Int32)\n def monday? : Bool\n def saturday? : Bool\n def sunday? : Bool\n def thursday? : Bool\n def to_s : String\n def tuesday? : Bool\n def value : Int32\n def wednesday? : Bool\nend\n\n# A time zone: the offsets from UTC a place has kept, and when it changed\n# from one to the next. `Location.load(\"Europe/Istanbul\")` reads the IANA\n# database the machine has; `Location.local` is the machine's own zone;\n# `Location.fixed(19800)` is an offset and nothing else; `Location::UTC` is\n# UTC. A `Time` carries one, and its date and clock are read in it.\n#\n# The database is the system's TZif files, RFC 8536: `$ZONEINFO` when it\n# names a directory, then `/usr/share/zoneinfo`, `/usr/share/lib/zoneinfo`\n# and `/etc/zoneinfo`. Versions 1 to 4 are read. A time before the first\n# change has the file's first offset, and one after the last has its POSIX\n# footer's rule, so a slim file answers for the year 2100 as a fat one does.\n# Leap seconds are not counted, as no clock this module reads counts them.\n#\n# `Location.local` is the `TZ` variable when it is set — the empty string\n# is UTC, a leading `:` is dropped, an absolute path is a TZif file, a name\n# is looked up in the database, and anything else is read as a POSIX TZ\n# string such as `EST5EDT,M3.2.0,M11.1.0` — and `/etc/localtime` when it is\n# not. Windows has no database to read: there `local` is the system's zone,\n# its rule from `GetTimeZoneInformation`, its zones named by their offsets,\n# and `load` reads `$ZONEINFO` alone. Where nothing answers, local is UTC.\n# The local zone is read once, the first time it is asked for.\n#\n# A location is built once and never changed, so a `Time` that holds one\n# can cross to another task.\npub class Location\n # One offset a location keeps: its abbreviation (`CEST`, `+03`), its\n # seconds east of UTC, and whether it is summer time.\n struct Zone\n def ==(other : Zone) : Bool\n def dst? : Bool\n def initialize(name : String, offset : Int32, dst : Bool)\n def name : String\n def offset : Int32\n def to_s : String\n end\n def ==(other : Location) : Bool\n def self.fixed(name : String, offset : Int32) : Location\n # An offset of *offset* seconds east of UTC and nothing else, named as\n # the offset is written: `+05:30`. Less than a day either way.\n def self.fixed(offset : Int32) : Location\n # One offset, never changing.\n def fixed? : Bool\n def hash : Int32\n def initialize(name : String, zones : Array(Zone), transitions : Array(Int64), transition_zones : Array(Int32), rule : TzRule | ::Nil)\n def inspect : String\n # The zone the IANA database names *name*: `Europe/Istanbul`,\n # `America/New_York`. `UTC` and the empty name are UTC and `Local` is\n # `Location.local`, without a file. A name that climbs out of the database\n # (`..`, a leading `/`, a `\\`) is refused, and so is one no directory has.\n def self.load(name : String) : Location | LocationError\n # The machine's zone; see the type's comment for where it comes from.\n def self.local : Location\n # The machine's own zone, the one `Location.local` answers.\n def local? : Bool\n # The zone *time* is in here.\n def lookup(time : Time) : Zone\n def lookup(unix_seconds : Int64) : Zone\n def name : String\n # A location from the 172 bytes of a Windows TIME_ZONE_INFORMATION, which\n # is what `Location.local` reads on Windows; it is pure so that every\n # platform can check the decoding with bytes made by hand. The struct is\n # the bias in minutes west of UTC (UTC = local + bias), then the standard\n # name, date and bias, then the daylight ones, all little-endian. The\n # three biases are signed LONGs: a zone east of UTC has a negative Bias\n # (Turkey's is -180) and a summer usually a DaylightBias of -60, so each\n # is read as the 32 bits' two's complement. Read as unsigned and then\n # converted, as it once was, -180 was 4294967116 and the conversion\n # panicked with an arithmetic overflow, issue #133.\n #\n # A date with a year of 0 is a rule - the wDay'th wDayOfWeek of wMonth,\n # 5 the last, at the wall time then in force - which is a POSIX\n # `Mm.w.d/time`; one with a month of 0 is no summer. A date with a year\n # is a single year's, which is not a rule at all, and it is read as no\n # summer rather than as every year's. The names are the offset's, `+03`\n # or `-05`, as tzdata writes a zone it has no letters for: the struct's\n # own are Windows' long names, \"Turkey Standard Time\", which are not\n # abbreviations and are localised.\n def self.read_windows_zone(name : String, data : String) : Location | LocationError\n # A location from the bytes of a TZif file, RFC 8536, named *name*.\n # Anything that is not one is refused with what was wrong and where.\n def self.read_zoneinfo(name : String, data : String) : Location | LocationError\n # The instant a wall clock here reads *wall* (seconds since 1970 as a\n # UTC clock would count them). A wall time a change repeats is the\n # earlier of the two instants; one a change skips is read on the clock\n # from before the change, which lands it past the gap: 02:30 on the\n # morning summer starts in Berlin is 03:30 CEST. Python's `fold=0` and\n # Go read them the same way. `Time.local` and a parse at a location ask\n # this.\n def resolve(wall : Int64) : Int64\n def to_s : String\n def utc? : Bool\nend\n\n# What `Location.load` and `Location.read_zoneinfo` answer when there is no\n# zone by that name, or the bytes are not a zone: a name the program was\n# handed or a file on the machine is not a bug in the program, so it is a\n# value and not a panic.\npub struct LocationError\n def initialize(message : String)\n def to_s : String\nend\n\npub struct Span\n def +(other : Span) : Span\n def -(other : Span) : Span\n def - : Span\n def <(other : Span) : Bool\n def <=(other : Span) : Bool\n def <=>(other : Span) : Int32\n def ==(other : Span) : Bool\n def >(other : Span) : Bool\n def >=(other : Span) : Bool\n # The seconds are an Int64, and a span that leaves it is refused here,\n # by name: `Span.seconds(Int64::MAX) + Span.seconds(1)` panicked with the\n # prelude's \"arithmetic overflow\", a sentence that named no span. The\n # wrapping sum is compared with an addend to see whether it wrapped; the\n # product is bounded before it is taken.\n def self.add_seconds(a : Int64, b : Int64) : Int64\n def days : Int64\n def self.days(n : Int64) : Span\n # Hashing follows `==`, the two parts mixed as a tuple's members are. Without\n # it every span hashed to its type: `Span.seconds(1).hash ==\n # Span.days(9).hash`, and a table of 16,000 spans took 0.95 s to fill where\n # the same pairs as tuples took 1 ms.\n def hash : Int32\n def hours : Int32\n def self.hours(n : Int64) : Span\n def initialize(seconds : Int64 = 0_i64, nanoseconds : Int32 = 0)\n def millisecond : Int32\n def milliseconds : Int32\n def self.milliseconds(n : Int64) : Span\n def minutes : Int32\n def self.minutes(n : Int64) : Span\n def self.mul_seconds(n : Int64, unit : Int64) : Int64\n def nanosecond : Int32\n def nanoseconds : Int32\n def self.nanoseconds(n : Int64) : Span\n def self.new(*, days : Int64 = 0_i64, hours : Int64 = 0_i64, minutes : Int64 = 0_i64, seconds : Int64 = 0_i64, milliseconds : Int64 = 0_i64, nanoseconds : Int64 = 0_i64) : Span\n def seconds : Int32\n def self.seconds(n : Int64) : Span\n def self.sub_seconds(a : Int64, b : Int64) : Int64\n def to_i : Int64\n def to_s : String\n def total_days : Float64\n def total_hours : Float64\n def total_milliseconds : Float64\n def total_minutes : Float64\n def total_nanoseconds : Float64\n def total_seconds : Float64\n def total_seconds_i64 : Int64\n def self.zero : Span\nend\n\npub struct Time\n alias DayOfWeek = Std::Time::DayOfWeek\n # A pattern of Crystal's directives, for writing a `Time` and reading one\n # back. Text that is not a directive is written as it is and must be\n # there, byte for byte, when reading; ASCII whitespace in the pattern is\n # written as it is and reads any run of whitespace, none included.\n #\n # %Y year, four digits or more, year -1 as -0001 %C year // 100\n # %y year % 100 %G %g %V the ISO 8601 week year, its last two digits,\n # and the week 01..53 %m month 01..12 %B January %b %h Jan\n # %d day 01..31 %e day \" 1\"..\"31\" %j day of the year 001..366\n # %H hour 00..23 %k \" 0\"..\"23\" %I 01..12 %l \" 1\"..\"12\" %p am %P AM\n # %M minute %S second %L milliseconds %3N %6N %9N the fraction to\n # 3, 6 and 9 digits %N nanoseconds, any number of digits when read\n # %A Sunday %a Sun %u day of the week, Monday 1..7 %w Sunday 0..6\n # %s seconds since the epoch %z +0530 %:z +05:30 %::z +05:30:00\n # %Z and %^Z: UTC at offset 0 and the offset, +05:30, at any other\n # %F %Y-%m-%d %T %X %H:%M:%S %R %H:%M %D %x %m/%d/%y\n # %r %I:%M:%S %P %c %a %b %e %H:%M:%S %Y %n newline %t tab %% %\n #\n # A number takes a flag between the `%` and its letter: `-` for no\n # padding (`%-d` is 5), `_` for spaces (`%_m` is \" 5\") and `0` for zeros\n # (`%0e` is 05); `^` upcases a name (`%^a` is SUN). The other library knows\n # `%-d`, `%-m`, `%_m` and the `^` names. The same flags on its other numbers\n # are this module's, where that library wrote the `%-` back out as text; a `%`\n # that starts no directive is text here as it is there.\n #\n # Reading takes up to a number's width in digits, fewer when the next byte\n # is not a digit, and a name in any case. `%Y` takes four digits when a\n # directive follows it (`%Y%m%d`), and when text or the end of the\n # pattern does (`%F`), an optional `-` and up to ten, so the years `to_s`\n # writes are the years it reads. A field outside its range, a day the\n # month does not have, a name that is not one and text after the pattern\n # are each a `TimeParseError`, never a panic. A day name is checked to be\n # one and not checked against the date, as Crystal, Go and Python read it.\n struct Format\n # The parts of an instant as they read at an offset, worked out once\n # per `format` rather than once per directive. *zone* is what `%Z`\n # writes, the location's abbreviation; with none it writes `UTC` at\n # offset 0 and the offset at any other.\n struct Fields\n def day : Int32\n def day_of_week : Int32\n def day_of_year : Int32\n def hour : Int32\n def initialize(time : Time, offset : Int32, zone : String | ::Nil)\n def minute : Int32\n def month : Int32\n def nanosecond : Int32\n def offset : Int32\n def second : Int32\n def unix : Int64\n def week : ::Tuple(Int64, Int32)\n def year : Int32\n def zone : String | ::Nil\n end\n # The HTTP date of RFC 9110 section 5.6.7. It is written as the\n # IMF-fixdate, always in GMT, `Sun, 06 Nov 1994 08:49:37 GMT`; it is\n # read in all three of the forms a recipient must accept: that one, the\n # obsolete RFC 850 `Sunday, 06-Nov-94 08:49:37 GMT`, and asctime's\n # `Sun Nov 6 08:49:37 1994`.\n module HTTP_DATE\n def self.format(time : Time) : String\n def self.parse(string : String) : Time | TimeParseError\n end\n # ISO 8601's calendar date, written `2016-02-15` as the date reads at\n # *offset* or in the time's own location; read extended or basic, and\n # as an ordinal or a week date, at *offset* since a date names no zone.\n module ISO_8601_DATE\n def self.format(time : Time, offset : Int32 | ::Nil = nil) : String\n def self.parse(string : String, offset : Int32 = 0) : Time | TimeParseError\n end\n # ISO 8601's date and time, written as RFC 3339 writes it and read with\n # every form of `ISO_8601_DATE` and `ISO_8601_TIME` joined by an\n # optional `T`. The zone is required, as Crystal requires it.\n module ISO_8601_DATE_TIME\n def self.format(time : Time, offset : Int32 | ::Nil = nil) : String\n def self.parse(string : String) : Time | TimeParseError\n end\n # ISO 8601's time of day and its zone, `04:35:50Z`; read as on\n # 0001-01-01, the day Crystal reads it on.\n module ISO_8601_TIME\n def self.format(time : Time, offset : Int32 | ::Nil = nil) : String\n def self.parse(string : String) : Time | TimeParseError\n end\n # Reads a text by a pattern or by one of the named grammars. Every step\n # answers whether it matched; the first that did not leaves its sentence\n # in `@error`, and `finish` answers that, so no text a program is handed\n # ends it in a panic.\n class Parser\n def accept(char : Char) : Bool\n def am_pm : Bool\n def at_end : Bool\n # RFC 5322's CFWS: whitespace and parenthesised comments, which nest\n # and in which `\\` quotes the next byte. Answers whether there was any;\n # a comment left open is the error, and the parse fails on it.\n def cfws : Bool\n def day_name : Bool\n def self.digit?(b : UInt8) : Bool\n # Up to *max* digits and at least one. Nil, with the sentence, when\n # there are none, or when they are past what an Int64 holds.\n def digits(max : Int32, what : String) : Int64 | ::Nil\n def directive(letter : Char, flag : Char, digits : Int32, wide : Bool) : Bool\n # Exactly *count* digits, or nil, reading nothing and saying nothing:\n # the caller names what was missing.\n def exact(count : Int32) : Int32 | ::Nil\n def expect(char : Char) : Bool\n def fail(what : String) : Bool\n # A numeric directive's field, read as its flag and its padding say.\n def field(width : Int32, pad : Char, flag : Char, what : String) : Int32 | ::Nil\n # The instant the fields name, or the first sentence a step left. The\n # fields are checked the way `Time.utc` checks them, with RFC 3339's\n # allowance: `:60` is a leap second, read as the second after `:59`,\n # the instant it names on every clock that has none. A text that\n # names no zone is read at *default_offset*, or failing that on\n # *location*'s wall clock; with a location the time answered is in it.\n def finish(default_offset : Int32 | ::Nil, location : Location | ::Nil = nil) : Time | TimeParseError\n # A fraction of a second: up to *max* digits, scaled to nanoseconds,\n # and for `%N` any digits past the ninth read and dropped.\n def fraction(max : Int32, rest : Bool) : Bool\n # RFC 9110 section 5.6.7's three forms of an HTTP date: the\n # IMF-fixdate `Sun, 06 Nov 1994 08:49:37 GMT`, the obsolete RFC 850\n # `Sunday, 06-Nov-94 08:49:37 GMT`, and asctime's `Sun Nov 6 08:49:37\n # 1994`, which has no zone and is GMT. Crystal's reading: a day name,\n # short or long, in any of the three; the four-digit year IIS writes\n # in the RFC 850 shape; and the zones RFC 2822 names where RFC 9110\n # writes GMT.\n def http_date : Bool\n def initialize(input : String)\n # ISO 8601's dates, extended or basic: 2016-02-15 and 20160215, the\n # ordinal 2016-046 and 2016046, and the week date 2016-W07-1 and\n # 2016W071.\n def iso_date : Bool\n def iso_date_time : Bool\n # ISO 8601's time of day, extended or basic: 04:35:50 and 043550, a\n # fraction of the second after `.` or `,`, or the minutes or the hour\n # alone with a fraction of their own (04:35.5 is 04:35:30).\n def iso_time : Bool\n def self.letter?(b : UInt8) : Bool\n def literal(b : UInt8) : Bool\n def month_name(short : Bool) : Bool\n # A run of ASCII letters and the index in *names* of the name it\n # spells, in any case: for *short*, the first three letters of a name\n # and no more; otherwise the start of a name, three letters or longer,\n # as Crystal reads `%B` and `%A`.\n def name(names : Array(String), short : Bool, what : String) : Int32 | ::Nil\n def need_cfws : Bool\n def number(max : Int32, what : String) : Int32 | ::Nil\n def offset_number(allow_colon : Bool, force_colon : Bool, allow_seconds : Bool) : Bool\n # A number blank padded, as `%e` writes \" 5\": one space stands for a\n # digit.\n def padded_number(max : Int32, what : String) : Int32 | ::Nil\n def pattern(pattern : String) : Bool\n # The byte *ahead* of the current one, or 0 past the end, as Crystal's\n # reader answers '\\0' there.\n def peek(ahead : Int32 = 0) : UInt8\n # RFC 9110's rule for a two-digit year (section 5.6.7): a year that\n # appears to be more than 50 years in the future is the most recent\n # past year with those last two digits. The candidate is taken in this\n # century and moved a century to land in the 100 years ending 50 years\n # from now, so the rule holds in 2090 too. The other library read 00..49 as 20xx\n # and the rest as 19xx, whatever the year it was read in.\n def self.recent_year(two_digits : Int64) : Int64\n # A sentence about fields that were all read, where no one byte is to\n # blame.\n def refuse(what : String) : TimeParseError\n # RFC 5322 section 3.3's date-time, with section 4.3's obsolete\n # two- and three-digit years and zone names, as Crystal's `RFC_2822`\n # reads it: `Mon, 15 Feb 2016 04:35:50 +0000`, the day name and the\n # seconds optional, comments anywhere whitespace goes.\n def rfc_2822 : Bool\n def same_letters?(start : Int32, name : String, size : Int32) : Bool\n def skip_spaces : Bool\n def self.space?(b : UInt8) : Bool\n def text(start : Int32, stop : Int32) : String\n # HH:MM:SS, as both HTTP and RFC 850 write the time of day.\n def time_of_day : Bool\n # One whitespace byte, as the HTTP grammars put between their parts.\n def whitespace : Bool\n # `%Y`: four digits when a directive follows it, and an optional `-`\n # and up to ten when text does, which is every year an Int32 holds.\n def year(wide : Bool) : Int64 | ::Nil\n # Exactly four digits, as the HTTP and ISO 8601 grammars write a year.\n def year4 : Bool\n # `%z`, `%:z` and `%::z` alike, as Crystal reads them: `Z`, `UTC`, or\n # an offset with or without its colons.\n def zone : Bool\n # `%Z`: an offset, or a name read up to the next whitespace. Only the\n # names of UTC itself are read: an abbreviation names no single zone\n # (CST is three of them), so any other is refused by name rather than\n # read as UTC or guessed at. A text whose zone is known is read at\n # that `Location` instead.\n def zone_name : Bool\n # RFC 5322's zone (section 3.3, and 4.3 for the obsolete names): an\n # offset of four digits, or a name. A military letter is read as\n # `-0000`, the offset of a time whose zone is not known, as section\n # 4.3 says to; a name the RFC does not give is refused, where Crystal\n # read it as UTC in silence. `UTC` is not the RFC's, and is read as\n # Crystal reads it.\n def zone_rfc2822 : Bool\n # ISO 8601's zone: `Z` or an offset.\n def zone_z_or_offset : Bool\n end\n # RFC 2822's date and time, `Mon, 15 Feb 2016 04:35:50 +0000`, as\n # Crystal writes it: the day of the month unpadded, the offset at the\n # end, the time's own unless another is given. RFC 2822 writes an offset\n # in whole minutes, so an offset with seconds is refused rather than\n # written as some other instant.\n module RFC_2822\n def self.format(time : Time, offset : Int32 | ::Nil = nil) : String\n def self.parse(string : String) : Time | TimeParseError\n end\n # RFC 3339, the module's own serialisation: `to_rfc3339` and\n # `parse_rfc3339` are this module's `format` and `parse`, and\n # `parse_rfc3339` panics with the sentence `parse` answers. Written at\n # *offset*, or at the time's own when it is whole minutes and in UTC\n # when it is not.\n module RFC_3339\n def self.format(time : Time, fraction_digits : Int32 = 0, offset : Int32 | ::Nil = nil) : String\n def self.parse(str : String) : Time | TimeParseError\n end\n # An offset is less than a day either side of UTC: the widest RFC 3339\n # and RFC 2822 write is 23:59, and a day or more is a date, not a zone.\n # An offset is the program's own, so a wrong one is a panic.\n def self.check_offset(offset : Int32) : Nil\n def format(time : Time, offset : Int32) : String\n def format(time : Time) : String\n # *offset*, seconds east of UTC, is the one a parse reads a text at when\n # the text names no zone, and the one `format` writes at. Nil, as for\n # Crystal's `Format.new(pattern)`, makes a text without a zone an error,\n # and `format` writes a time in its own location.\n def initialize(pattern : String, offset : Int32 | ::Nil = nil)\n # A text that names no zone is read on *location*'s wall clock, and\n # every time is read and written in *location*.\n def initialize(pattern : String, location : Location)\n # The directive at the `%` at byte *i* of *pattern*: the byte after it,\n # its letter, its flag, the colons of `%:z` and `%::z`, and the digits of\n # `%3N`. A `%` that starts no directive answers the letter '\\0' and the\n # byte after the `%`: it is text, and so is what follows it, as Crystal\n # writes `%Q` back out as `%Q`.\n def self.lex(pattern : String, i : Int32) : ::Tuple(Int32, Char, Char, Int32, Int32)\n def location : Location | ::Nil\n def self.name(io : String::Builder, text : String, flag : Char) : Nil\n # A number padded to *width* with *pad*, '\\0' for none, unless a flag\n # says otherwise. The sign of a negative year goes in front of the\n # padding, as `-0001`, the way `to_rfc3339` writes it.\n def self.number(io : String::Builder, value : Int64, width : Int32, pad : Char, flag : Char) : Nil\n def self.numeric?(letter : Char) : Bool\n def offset : Int32 | ::Nil\n # `+hh:mm` or `+hhmm`, and the seconds never (0), when there are any (1)\n # or always (2), the way Crystal's `Zone#format` writes an offset.\n def self.offset_text(offset : Int32, colon : Bool, seconds : Int32) : String\n def parse(string : String, offset : Int32 | ::Nil) : Time | TimeParseError\n def parse(string : String) : Time | TimeParseError\n def pattern : String\n def self.plain?(letter : Char) : Bool\n def self.textual?(letter : Char) : Bool\n def self.twelve(hour : Int32) : Int64\n def self.write(io : String::Builder, pattern : String, f : Fields) : Nil\n def self.write_directive(io : String::Builder, letter : Char, flag : Char, colons : Int32, digits : Int32, f : Fields) : Nil\n end\n alias Location = Std::Time::Location\n alias Span = Std::Time::Span\n # The sum is checked before it is taken, so a span too long for the\n # seconds is refused as one; a sum that fits but leaves the years an\n # Int32 names is refused by the constructor. A span is exact: a day\n # added across the night summer begins is 24 hours, and the clock reads\n # an hour later than it did.\n def +(span : Span) : Time\n def -(span : Span) : Time\n def -(other : Time) : Span\n def <(other : Time) : Bool\n def <=(other : Time) : Bool\n def <=>(other : Time) : Int32\n def ==(other : Time) : Bool\n def >(other : Time) : Bool\n def >=(other : Time) : Bool\n # The ISO 8601 week-numbering year and week this day falls in, as\n # Crystal's `calendar_week` answers: 2021-01-01 is in week 53 of 2020.\n def calendar_week : ::Tuple(Int32, Int32)\n # Converts days since 1970-01-01 into {year, month, day}.\n def self.civil_from_days(z : Int64) : ::Tuple(Int32, Int32, Int32)\n def day : Int32\n def day_of_week : DayOfWeek\n def day_of_year : Int32\n def self.days_from_civil(y : Int64, m : Int32, d : Int32) : Int64\n def self.days_in_month(year : Int32, month : Int32) : Int32\n def self.days_in_year(year : Int32) : Int32\n # Number of days between 1970-01-01 and the given civil date.\n # Follows Howard Hinnant's Gregorian calendar algorithm.\n def self.floor_div(a : Int64, b : Int64) : Int64\n def self.floor_mod(a : Int64, b : Int64) : Int64\n def self.from_unix(seconds : Int64, nanoseconds : Int32 = 0) : Time\n def self.from_unix_ms(milliseconds : Int64) : Time\n # Hashing follows `==`, as `Span`'s does. Without it every time hashed to its\n # type: `Time.unix(1).hash == Time.unix(1000000, 5).hash`, and a table of\n # 16,000 times took 1.2 s to fill where the same pairs as tuples took 1 ms.\n def hash : Int32\n def hour : Int32\n # The same instant read in *location*: `Time.utc(2024, 7, 1, 12).in(berlin)`\n # is 14:00 CEST.\n def in(location : Location) : Time\n def initialize(seconds : Int64, nanoseconds : Int32 = 0, location : Location = Location::UTC)\n # As the other library writes it: the date and time, the nanoseconds\n # when there are any, and `Z`. There was no `inspect`, so it was `to_s`,\n # and `Time.utc(2024, 1, 1, 0, 0, 0, nanosecond: 5)` inspected as\n # `2024-01-01 00:00:00 UTC`, the same as `Time.utc(2024, 1, 1)`, which\n # `==` tells apart from it. Out of UTC the offset follows, and the\n # location's name when it is more than an offset:\n # `2016-02-15 11:20:30 +01:00 Europe/Berlin`.\n def inspect : String\n # The ISO 8601 week-numbering year and week of a date, from its year, day\n # of the month, day of the year and day of the week (Monday is 1). This is\n # Crystal's computation: the week is the count of Mondays up to the day,\n # and the first days of January or the last of December can belong to a\n # week of the year next to it. The year is an Int64 because the first days\n # of the smallest Int32 year belong to the year before it.\n def self.iso_week(year : Int64, day : Int32, day_of_year : Int32, day_of_week : Int32) : ::Tuple(Int64, Int32)\n def self.leap_year?(year : Int32) : Bool\n # The time a clock in *location* shows as the date and time given, the\n # machine's own zone when none is. Its fields are checked as `utc` checks\n # them. A wall time a change of offset repeats, the hour after summer\n # ends, is the earlier of the two; one a change skips, the hour summer\n # begins with, is read past the gap (see `Location#resolve`).\n def self.local(year : Int32, month : Int32, day : Int32, hour : Int32 = 0, minute : Int32 = 0, second : Int32 = 0, millisecond : Int32 = 0, nanosecond : Int32 = 0, location : Location = Location.local) : Time\n # Now, read in *location*.\n def self.local(location : Location) : Time\n # Now, in the machine's own zone (`Location.local`).\n def self.local : Time\n def local? : Bool\n # The zone the date and clock are read in, and its offset from UTC at\n # this instant in seconds east. `==`, `<=>` and `hash` are the instant's\n # and do not look at either: noon in London and 13:00 in Paris are the\n # same time.\n def location : Location\n def millisecond : Int32\n def milliseconds : Int32\n def minute : Int32\n def self.monotonic : Span\n def self.monotonic_nanoseconds : UInt64\n def month : Int32\n def nanosecond : Int32\n def nanoseconds : Int32\n def self.new(year : Int32, month : Int32, day : Int32, hour : Int32 = 0, minute : Int32 = 0, second : Int32 = 0, millisecond : Int32 = 0, nanosecond : Int32 = 0) : Time\n def offset : Int32\n def self.pad2(n : Int32) : String\n def self.pad3(n : Int32) : String\n # Four digits, and a negative year keeps its sign in front of them rather\n # than inside: written as `n < 10 ? \"000\" + n.to_s`, year -1 printed\n # `000-1-01-01`, which is neither a year nor a date. ISO 8601 spells it\n # `-0001`, and the conversions themselves were right all along - the epoch\n # round-trips through it.\n def self.pad4(n : Int32) : String\n def self.pad6(n : Int32) : String\n def self.pad9(n : Int32) : String\n # Reads *string* by *pattern*'s directives (see `Time::Format`). A text\n # that names no zone is read at *offset* seconds east of UTC, as Crystal's\n # `Time.parse` reads one at the location it is given.\n def self.parse(string : String, pattern : String, offset : Int32) : Time | TimeParseError\n # Reads *string* by *pattern* in *location*: a text that names no zone is\n # the wall clock there, its offset the one the location kept at that date\n # (a wall time a change repeats or skips is read as `Time.local` reads\n # it), and the time answered is in *location* whether the text named a\n # zone or not.\n def self.parse(string : String, pattern : String, location : Location) : Time | TimeParseError\n def self.parse_iso8601(string : String) : Time | TimeParseError\n # `parse` in the machine's own zone.\n def self.parse_local(string : String, pattern : String) : Time | TimeParseError\n # Any of RFC 9110's three HTTP date forms; see `Time::Format::HTTP_DATE`.\n def self.parse_rfc1123(string : String) : Time | TimeParseError\n def self.parse_rfc2822(string : String) : Time | TimeParseError\n # Parses an RFC 3339 / ISO 8601 string into a UTC Time, and panics with the\n # parser's sentence when it is not one. Handles forms like:\n # 2024-02-29T12:30:45Z\n # 2024-02-29T12:30:45.123Z\n # 2024-02-29T12:30:45.123456789Z\n # 2024-02-29 12:30:45Z\n # 2024-02-29T12:30:45+05:00\n # 2024-02-29T12:30:45-08:00\n # -0001-01-01T00:00:00Z and 12345-06-07T08:09:10Z, as `to_rfc3339` writes them\n # `Time::Format::RFC_3339.parse` reads the same grammar and answers a\n # `TimeParseError` instead, for text the program did not write itself.\n def self.parse_rfc3339(str : String) : Time\n # `parse` at UTC when the text names no zone.\n def self.parse_utc(string : String, pattern : String) : Time | TimeParseError\n def second : Int32\n def seconds : Int64\n # Moves the date by calendar years and months, then by weeks and days,\n # then by the smaller units, in that order, as Crystal's `shift` does. A\n # day the month does not have becomes its last: `Time.utc(2024, 1,\n # 31).shift(months: 1)` is 2024-02-29. The date moves on the wall clock\n # of the time's location, so the time of day is kept across a change of\n # offset - a day after 12:00 on the Saturday summer begins is 12:00 on\n # Sunday, 23 hours later - and the hours, minutes, seconds and\n # nanoseconds are exact. A year past an Int32 is refused with the\n # constructor's sentence, and a span the Int64 seconds do not hold with\n # `Span`'s.\n def shift(*, years : Int32 = 0, months : Int32 = 0, weeks : Int64 = 0_i64, days : Int64 = 0_i64, hours : Int64 = 0_i64, minutes : Int64 = 0_i64, seconds : Int64 = 0_i64, nanoseconds : Int64 = 0_i64) : Time\n def to_local : Time\n # RFC 1123's date as HTTP writes it, RFC 9110's IMF-fixdate, always in GMT:\n # `Sun, 06 Nov 1994 08:49:37 GMT`.\n def to_rfc1123 : String\n # RFC 2822's date, `Mon, 15 Feb 2016 04:35:50 +0000`, at *offset*, or at\n # the time's own offset when none is given.\n def to_rfc2822(offset : Int32 | ::Nil = nil) : String\n # RFC 3339 at the time's own offset: `Z` at UTC, `+02:00` in CEST. An\n # offset RFC 3339 cannot write, one with seconds in it as local mean time\n # before standard time had, is written as the same instant in UTC.\n def to_rfc3339(fraction_digits : Int32 = 0) : String\n # The instant written as it reads at *offset* seconds east of UTC:\n # `to_s(\"%F %T %:z\", 19800)` is the wall clock in India.\n def to_s(format : String, offset : Int32) : String\n # The time written by *format*'s directives (see `Time::Format`) as its\n # own location reads it; `%Z` is the zone's abbreviation, `CEST`.\n def to_s(format : String) : String\n # `2016-02-15 10:20:30 UTC`, and in any other location the offset in\n # place of `UTC`: `2016-02-15 11:20:30 +01:00`.\n def to_s : String\n def to_unix : Int64\n def to_unix_f : Float64\n # Refused with the module's sentence past the milliseconds an Int64\n # holds, some 292 million years either side of 1970, where the multiply\n # was the prelude's bare \"arithmetic overflow\" for a year `Time.utc`\n # accepts, 300,000,000. Both edges answer to the last millisecond that\n # fits: the lowest 808 an Int64 holds were refused, so\n # `Time.unix_ms(Int64::MIN).to_unix_ms` panicked. A negative second is\n # taken one up and its milliseconds from 1000 down, so the product fits\n # too.\n def to_unix_ms : Int64\n def to_utc : Time\n def self.unix(seconds : Int64, nanoseconds : Int32 = 0) : Time\n def self.unix_ms(milliseconds : Int64) : Time\n def self.utc(year : Int32, month : Int32, day : Int32, hour : Int32 = 0, minute : Int32 = 0, second : Int32 = 0, millisecond : Int32 = 0, nanosecond : Int32 = 0) : Time\n def self.utc : Time\n def utc? : Bool\n # Both through the runtime's clock (`IyiClock`), the vDSO with the\n # syscall behind it.\n def self.wall_clock_nanoseconds : UInt64\n # The instant of a day of an ISO 8601 week, as Crystal's `week_date` makes\n # it: `Time.week_date(2016, 1, 1)` is Monday 2016-01-04. A week the year\n # does not have and a day past Sunday are refused, where the other library answered\n # a day of some other week.\n def self.week_date(year : Int32, week : Int32, day_of_week : Int32, hour : Int32 = 0, minute : Int32 = 0, second : Int32 = 0, nanosecond : Int32 = 0) : Time\n # Days since 1970-01-01 of a day of an ISO 8601 week. Week 1 is the week\n # holding January 4th, so its Monday is the Monday on or before that day.\n def self.week_date_days(year : Int32, week : Int32, day_of_week : Int32) : Int64\n # A year has 53 ISO 8601 weeks when it starts on a Thursday, or on a\n # Wednesday in a leap year: either way it has 53 Thursdays, and a week\n # belongs to the year that holds its Thursday.\n def self.weeks_in_year(year : Int32) : Int32\n def year : Int32\n # The zone this instant is in at its location: its abbreviation, offset\n # and whether it is summer time.\n def zone : Location::Zone\nend\n\n# What `Time::Format#parse` and the named parsers answer when the text is not\n# a time the pattern describes. Bad input is the caller's to handle, not a\n# panic: a header or a log line from somewhere else is not a bug in the\n# program reading it. `message` is the sentence, naming the byte the parser\n# stood at and the text; `position` is that byte, counted from 0.\npub struct TimeParseError\n def initialize(message : String, input : String, position : Int32)\n def input : String\n def position : Int32\n def to_s : String\nend\n\nimpl Error for TimeParseError\n def message : String\nend\n\nimpl Std::Traits::Comparable for Span\n def <=>(other : self) : Int32\nend\n\nimpl Error for LocationError\n def message : String\nend\n\nimpl Std::Traits::Comparable for Time\n def <=>(other : self) : Int32\nend\n" }, { "name": "std/tls", "path": "src/std/tls.iyi", - "sourceSha256": "6e485b1800591b3613e9bcb9b494f71ae0dbe9ecbeceafcc509475da662f98a2", + "sourceSha256": "d474c4c3462c72d5d68c240060f06f3979f99fad8e125ab880909924bd93b83d", "surface": "module std/tls\n\n# The alerts of RFC 8446 section 6, by the code each is sent as.\npub struct TlsAlert\n def initialize\nend\n\n# A connection to a server, its handshake done.\npub class TlsClient < TlsConnection\n # A CertificateStatus's body (RFC 6066 section 8): status_type ocsp (1),\n # then the OCSPResponse's DER, which is not empty.\n def self.certificate_status(data : Array(UInt8)) : Array(UInt8) | TlsError\n # A connection to *host* on *port*, its handshake done and the server's\n # certificate checked for *host* - or, with a *session* that holds a\n # ticket for *host* and *port* under *config* (`TlsSession` says which\n # fit), resumed, and the tickets the server sends kept there.\n # *early_data* goes with the ClientHello (0-RTT, RFC 8446 section 2.3)\n # when the ticket taken allows that much, and after the handshake when it\n # did not, or the server refused it - unless the server then chose\n # another protocol by ALPN than the ticket's, which section 4.2.10 says\n # it must not be resent for; `early_data` says which. Early data can be\n # replayed by whoever sees it (section 8): send nothing early that is not\n # safe to run twice.\n def self.connect(host : String, port : Int32, config : TlsConfig = TlsConfig.new, timeout_ms : Int32 = -1, session : TlsSession | ::Nil = nil, early_data : String | Array(UInt8) | Nil = nil) : TlsClient | TlsError\n protected def early_bytes=(bytes : Array(UInt8) | ::Nil) : Array(UInt8) | ::Nil\n # What became of the early data given to `connect` or `over`.\n def early_data : TlsEarlyData\n def handshake : TlsError | ::Nil\n def initialize(socket : IyiSocket, host : String, config : TlsConfig, session : TlsSession | ::Nil = nil)\n # The handshake over a connected *socket*, for *host*, done within\n # *timeout_ms* (none when 0); *session* and *early_data* as for\n # `connect`.\n def self.over(socket : IyiSocket, host : String, config : TlsConfig = TlsConfig.new, timeout_ms : Int32 = HANDSHAKE_TIMEOUT_MS, session : TlsSession | ::Nil = nil, early_data : String | Array(UInt8) | Nil = nil) : TlsClient | TlsError\n # *host* in the form RFC 6066 section 3 gives server_name: \"the\n # fully qualified DNS hostname\", ASCII, \"without a trailing dot\" - a\n # final dot dropped and a name in Unicode written as its A-labels by\n # IDNA, which is also the form `X509` matches a certificate's names in.\n # Before, `bücher.example.` went out as its UTF-8 bytes and its dot, and\n # a server that holds names to the RFC refused it (OpenSSL's s_server\n # with `-servername_fatal`: alert unrecognized_name). An ASCII name keeps\n # its case, as DNS ignores it. A name IDNA refuses, or nothing but a dot,\n # is an error before anything is sent.\n def self.server_name_for(host : String) : String | TlsError\n def server_side? : Bool\nend\n\n# What a connection is checked against and offers.\npub class TlsConfig\n # Protocols offered by ALPN (RFC 7301), most wanted first: `[\"h2\",\n # \"http/1.1\"]`. None offered when empty.\n def alpn : Array(String)\n # Protocols offered by ALPN (RFC 7301), most wanted first: `[\"h2\",\n # \"http/1.1\"]`. None offered when empty.\n def alpn=(value : Array(String)) : Array(String)\n # A new configuration holding what this one holds - the same roots and\n # identity objects, a copy of `alpn` - for a part to be changed in it\n # and not here: std/http's client makes the configuration an HTTPS\n # record's `ech` and protocols ask for so. It is another object, and\n # what was made under this one (`TlsMadeUnder`) is not carried under it.\n def dup : TlsConfig\n # An ECHConfigList (RFC 9849 section 4) - a DNS HTTPS record's `ech`\n # value, or a refusal's `TlsError#retry_configs` - to encrypt the\n # ClientHello under: the name connected to, ALPN and the ticket go in\n # the encrypted ClientHelloInner, and the ClientHelloOuter around it\n # names the config's public_name. The handshake holds only where the\n # server took it; one that did not is refused with ech_required (the\n # module's header). The first ECHConfig whose version, KEM and a suite\n # std/hpke has, and whose public_name is a host name, is taken; one list\n # holding none is refused before anything is sent, as is TLS 1.2 beside\n # it (`min_version`). A config is one server's: none when nil.\n def ech_configs : Array(UInt8) | ::Nil\n # An ECHConfigList (RFC 9849 section 4) - a DNS HTTPS record's `ech`\n # value, or a refusal's `TlsError#retry_configs` - to encrypt the\n # ClientHello under: the name connected to, ALPN and the ticket go in\n # the encrypted ClientHelloInner, and the ClientHelloOuter around it\n # names the config's public_name. The handshake holds only where the\n # server took it; one that did not is refused with ech_required (the\n # module's header). The first ECHConfig whose version, KEM and a suite\n # std/hpke has, and whose public_name is a host name, is taken; one list\n # holding none is refused before anything is sent, as is TLS 1.2 beside\n # it (`min_version`). A config is one server's: none when nil.\n def ech_configs=(value : Array(UInt8) | ::Nil) : Array(UInt8) | ::Nil\n # Whether a ClientHello without `ech_configs` carries a GREASE\n # encrypted_client_hello (RFC 9849 section 6.2): random bytes of an\n # encrypted one's shape, so that one that is real does not stand out.\n # On by default, as Chrome and Firefox send it.\n def ech_grease : Bool\n # Whether a ClientHello without `ech_configs` carries a GREASE\n # encrypted_client_hello (RFC 9849 section 6.2): random bytes of an\n # encrypted one's shape, so that one that is real does not stand out.\n # On by default, as Chrome and Firefox send it.\n def ech_grease=(value : Bool) : Bool\n # The certificate and key sent to a server that asks for one (mutual\n # TLS), when the request names its scheme; none sent when nil.\n def identity : TlsIdentity | ::Nil\n # The certificate and key sent to a server that asks for one (mutual\n # TLS), when the request names its scheme; none sent when nil.\n def identity=(value : TlsIdentity | ::Nil) : TlsIdentity | ::Nil\n def initialize\n # The oldest version spoken. TLS 1.3 by default, and then a server that\n # answers in 1.2 is refused as one that does not speak 1.3; `TLS12`\n # offers 1.2 beside it (see the module's header for what it requires).\n def min_version : TlsVersion\n # The oldest version spoken. TLS 1.3 by default, and then a server that\n # answers in 1.2 is refused as one that does not speak 1.3; `TLS12`\n # offers 1.2 beside it (see the module's header for what it requires).\n def min_version=(value : TlsVersion) : TlsVersion\n # The time certificates are checked at; now when nil.\n def now : Time | ::Nil\n # The time certificates are checked at; now when nil.\n def now=(value : Time | ::Nil) : Time | ::Nil\n # Whether X25519MLKEM768 is offered, its share sent beside X25519's (the\n # module's header has what it costs). On by default, as OpenSSL 3.5,\n # Chrome and Firefox have it; off, the ClientHello offers X25519 and\n # secp256r1 alone.\n def post_quantum : Bool\n # Whether X25519MLKEM768 is offered, its share sent beside X25519's (the\n # module's header has what it costs). On by default, as OpenSSL 3.5,\n # Chrome and Firefox have it; off, the ClientHello offers X25519 and\n # secp256r1 alone.\n def post_quantum=(value : Bool) : Bool\n # Whether a server's certificate must come with a stapled OCSP response\n # (RFC 6066's status_request, asked for by every ClientHello): with it,\n # a server that staples none is refused (bad_certificate_status_response),\n # as one whose certificate carries the TLS Feature extension's\n # status_request (RFC 7633, \"must staple\") always is. Off by default: a\n # staple that comes is checked either way (the module's header).\n def require_staple : Bool\n # Whether a server's certificate must come with a stapled OCSP response\n # (RFC 6066's status_request, asked for by every ClientHello): with it,\n # a server that staples none is refused (bad_certificate_status_response),\n # as one whose certificate carries the TLS Feature extension's\n # status_request (RFC 7633, \"must staple\") always is. Off by default: a\n # staple that comes is checked either way (the module's header).\n def require_staple=(value : Bool) : Bool\n # The roots a server's chain must reach; the system's when nil, read\n # once for the whole process and kept, since reading them is most of a\n # handshake's own cost.\n def roots : Array(Certificate) | ::Nil\n # The roots a server's chain must reach; the system's when nil, read\n # once for the whole process and kept, since reading them is most of a\n # handshake's own cost.\n def roots=(value : Array(Certificate) | ::Nil) : Array(Certificate) | ::Nil\n def trusted_roots : Array(Certificate) | TlsError\nend\n\n# What either end of a connection does once it has keys: records read and\n# written under them, alerts, KeyUpdate, close_notify. `TlsClient` and\n# `TlsServer` are its two ends; each runs its own half of the handshake.\npub abstract class TlsConnection\n # Closes the socket at once: no close_notify, and nothing read past.\n def abort : Nil\n # The protocol chosen by ALPN, if one was.\n def alpn_protocol : String | ::Nil\n def cipher_suite : String\n # The connection ended: `close_write` - close_notify, then the sending\n # half shut - and what the peer still sends read and dropped, until its\n # close_notify or the socket's end, as long as it keeps coming\n # (`DRAIN_GAP_MS`), `DRAIN_MS` and `DRAIN_BYTES` at most; then the\n # socket closed. A socket closed with bytes unread in it is reset rather\n # than ended, and the reset takes from the\n # peer what it had received and not yet read: closed at once on a\n # request body it had not read, an HTTPS server lost its whole answer to\n # Python's http.client in each of 50 runs, and curl the end of it in\n # each of 50 (bench/std_http_exercise.sh, \"a close while the client\n # still sends\"). Nothing is waited for once the peer's close_notify has\n # been read: nothing comes after it. A socket already closed under the\n # connection leaves only the close: no alert, and nothing read. `abort`\n # is the close without any of this.\n def close : Nil\n # RFC 8446 section 6.1's end of this side's writing: close_notify, then\n # the socket's sending half shut (`IyiSocket#close_write`), so the peer\n # reads the alert and then the end of the stream. The reading half\n # stays: `read` goes on answering what the peer sends, until its own\n # close_notify (\"\") or the socket's end. `write` and `key_update` are\n # refused after it, and `close` or `abort` still gives the socket back.\n # TLS 1.2 has no such half: RFC 5246 section 7.2.1 has a peer answer a\n # close_notify with its own and close at once, discarding what it had\n # yet to write, so after this a 1.2 connection reads what was already\n # on its way, then the peer's close_notify. An error when the alert or\n # the shutdown could not be made, or when the socket was already closed\n # under the connection; the connection is still to be closed. A flight\n # still held (`hold`) is a handshake cut short, and is dropped, as\n # `fail` drops it: the close_notify goes alone.\n def close_write : TlsError | ::Nil\n # Whether the connection is over: closed, or its socket closed under\n # it - std/http's server closes the sockets of the connections it waits\n # on when it stops, a connection handed over by an upgrade among them -\n # after which a read or a write answers that it is closed rather than\n # reaching the socket, whose own read and write refuse a closed socket\n # by panicking.\n def closed? : Bool\n # Whether the handshake ran on the ClientHelloInner an encrypted\n # ClientHello carried (RFC 9849): the client's ECH taken - so the name,\n # the protocols and the ticket it asked with went encrypted, and the\n # server read them, not the ClientHelloOuter's.\n def ech_accepted? : Bool\n # *length* bytes bound to this connection, for *label* and *context*\n # (RFC 8446 section 7.5, RFC 5705's interface): the same at both ends and\n # nowhere else - a channel binding, or keys for another protocol riding\n # on this one. No context is the empty one, as TLS 1.3 has it; in TLS\n # 1.2 (RFC 5705 section 4, over the extended master secret) an empty\n # context is none, as OpenSSL's exporter takes it when told of none.\n def export_keying_material(label : String, context : Array(UInt8) = [] of UInt8, length : Int32 = 32) : Array(UInt8)\n # The alert the error names (its last parenthesised word), sent before\n # the socket closes, by either end (RFC 8446 section 6.2): under the keys\n # this side writes with, or in the clear before there are any. None for\n # an alert the peer sent, whose sentence names no alert of this side's.\n # no_renegotiation goes as the warning it always is (RFC 5246 section\n # 7.2.2); every other, fatal.\n # Then either end shuts its sending half, so the peer reads the alert\n # and then the end of the stream, and reads and drops what the peer\n # sent or goes on sending until it closes, `LINGER_BYTES` and\n # `LINGER_MS` at most. A close with bytes unread, or one that bytes\n # still on their way reach, is a reset, and Windows throws away what a\n # reset connection received and had not read, the alert with it: a\n # client refused for its certificate read \"reset\", not why, and the\n # server lost a client's ech_required in 18 and 15 of 20 runs\n # (bench/std_tls_ech_oracle.go). A client closed at once on a server's\n # 0.5-RTT records reset the connection in each of 100 refusals on Linux\n # and on darwin (bench/std_tls_server.py's untrusted-late), where the\n # server still read the alert, even read 300 ms after the reset; shut\n # and lingering, the alert and then the end of the stream in each of\n # 100. A peer that closes once it has the alert ends the linger then -\n # OpenSSL's s_server 0.2 ms after it, on darwin - and one that goes on\n # sending and never closes holds a refusal for the second\n # (untrusted-hold: 1000 to 1001 ms on Linux, 1001 to 1005 on darwin).\n protected def fail(error : TlsError) : TlsError\n def self.group_name(group : Int32) : String\n def initialize(socket : IyiSocket, peer : String, place : String)\n # Records one write key carries before the next (RFC 8446 section 5.5:\n # AES-GCM's is 2^24.5 full records; ChaCha20-Poly1305's far more): past\n # it, the next write moves to new keys first - or, in TLS 1.2, which has\n # no KeyUpdate, is refused, and the connection with it. Settable, for a\n # test.\n def key_limit : UInt64\n # Records one write key carries before the next (RFC 8446 section 5.5:\n # AES-GCM's is 2^24.5 full records; ChaCha20-Poly1305's far more): past\n # it, the next write moves to new keys first - or, in TLS 1.2, which has\n # no KeyUpdate, is refused, and the connection with it. Settable, for a\n # test.\n def key_limit=(value : UInt64) : UInt64\n # Moves this side's writes to new keys (section 4.6.3), asking the peer\n # to move its own when *request*: what `write` does by itself at\n # `key_limit`, for a program that wants it sooner. TLS 1.2 has no\n # KeyUpdate: there it is a `TlsError`, and the connection stays open.\n def key_update(request : Bool = false) : TlsError | ::Nil\n # The milliseconds left of the deadline `within` set, 0 once it has\n # passed; nil when there is none.\n protected def left_ms : Int32 | ::Nil\n # The key exchange group the handshake agreed on: \"X25519MLKEM768\",\n # \"X25519\", \"secp256r1\" or \"secp384r1\". One name for it, the one RFC\n # 8446 gives the field (NamedGroup); `key_exchange`, which the P-384 work\n # first called it, is KeyShareEntry's name for the share's bytes.\n def named_group : String\n # The OCSP response stapled to the server's certificate (RFC 6066, RFC\n # 8446 section 4.4.2.1) and the status it gives - for a client, checked\n # (std/ocsp); for a server, the one it sent. Nil when none was.\n def ocsp_response : OcspResponse | ::Nil\n def ocsp_status : OcspCertStatus | ::Nil\n # The chain the peer's certificate was checked through, leaf first: the\n # server's, for a client; the client's, for a server that asked for one.\n def peer_certificates : Array(Certificate)\n # Up to *max* bytes the peer sent: \"\" once it has closed with\n # close_notify. The socket's read timeout bounds the whole call.\n def read(max : Int32 = 16384) : String | TlsError\n # How long a read or a write of the connection under it may wait.\n def read_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\n # Whether the handshake resumed a session (see `TlsSession`).\n def resumed? : Bool\n # Whether this is the server's end.\n abstract def server_side? : Bool\n # The version negotiated: \"TLSv1.3\", or \"TLSv1.2\" where one end asked\n # for it and the other spoke nothing newer.\n def version : String\n # *block* under a deadline *ms* from now (none when 0), cleared after.\n protected def within(ms : Int32, &block : (-> T)) : T forall T\n # Sends *data*, in records of at most 2^14 bytes; answers its size.\n # Refused once `close_write` has sent this side's close_notify, and in\n # TLS 1.2 once the peer's has been read (see `read_now`).\n def write(data : String | Array(UInt8)) : Int32 | TlsError\n def write_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\nend\n\n# What became of the early data a client was given (`TlsClient.connect`'s\n# *early_data*).\npub enum TlsEarlyData : Int32\n Unused = 0\n Accepted = 1\n Resent = 2\n Late = 3\n Withheld = 4\nend\n\n# One ECHConfig (RFC 9849 section 4), of version 0xfe0d, as read from an\n# ECHConfigList: what a client encrypts its ClientHello under, and what a\n# server's `TlsEchKey` publishes.\npub class TlsEchConfig\n def config_id : Int32\n # Its extensions (section 4.2), each its type and data.\n def extensions : Array(::Tuple(Int32, Array(UInt8)))\n # The suite a client seals under: the first of `suites` std/hpke has (the\n # export-only AEAD seals nothing), under the config's KEM - or nil for a\n # config this client passes over: a KEM std/hpke does not have, a\n # public_name that is not a host name (section 6.1.7), an extension\n # twice, or a mandatory one - its type's high bit set - which this does\n # not know (section 4.2; none is defined yet).\n def hpke_suite : HpkeSuite | ::Nil\n def initialize(raw : Array(UInt8), config_id : Int32, kem_id : Int32, public_key : Array(UInt8), suites : Array(::Tuple(Int32, Int32)), maximum_name_length : Int32, public_name : String, extensions : Array(::Tuple(Int32, Array(UInt8))))\n def kem_id : Int32\n def maximum_name_length : Int32\n # The ECHConfigs of version 0xfe0d an ECHConfigList holds, in its order -\n # one of another version passed over by its length, as section 4 says -\n # or a decode_error naming what does not read: a list cut short or with\n # bytes after it, an empty one, or a config of this version whose fields\n # break their bounds.\n def self.parse_list(list : Array(UInt8)) : Array(TlsEchConfig) | TlsError\n def public_key : Array(UInt8)\n def public_name : String\n # Whether *name* is a public_name a client takes (section 6.1.7): LDH\n # labels (RFC 5890 section 2.3.1) of 1 to 63 bytes, no hyphen at either\n # end of one, joined by dots, 255 bytes at most - and its last label\n # neither all digits nor \"0x\" and hexadecimal digits, which a URL parser\n # reads as an IPv4 address.\n def self.public_name?(name : String) : Bool\n # The whole ECHConfig - version, length and contents - as it came: what\n # the HPKE info is made of (section 6.1), and a list of them.\n def raw : Array(UInt8)\n # `parse_list`'s answer, its refusal a sentence.\n def self.read_list(list : Array(UInt8)) : Array(TlsEchConfig) | String\n # The HPKE KDF and AEAD a client may seal under, by their code points.\n def suites : Array(::Tuple(Int32, Int32))\nend\n\n# An ECH key a server holds (RFC 9849 section 7.1): an ECHConfig and the\n# HPKE private key for the public key in it. `TlsServerConfig#ech_keys`\n# holds several: the current one and the ones before it that clients may\n# still hold from DNS, each with a config_id of its own (section 4.1), so\n# that a ClientHello names the one to open it with.\npub class TlsEchKey\n def config : TlsEchConfig\n # The ECHConfigList of *keys*' configs, in their order: what a DNS HTTPS\n # record's `ech` carries (RFC 9848), and a client's `ech_configs`.\n def self.config_list(keys : Array(TlsEchKey)) : Array(UInt8)\n # The key a server kept as its `config.raw` and `private_key.bytes`: one\n # ECHConfig, not a list, of version 0xfe0d and a KEM std/hpke has, that\n # a client may use; and the KEM's private key (RFC 9180 section 7.1.2)\n # for the public key in it - or why not.\n def self.from_bytes(config : Array(UInt8), private_key : Array(UInt8)) : TlsEchKey | TlsError\n # A new key and its ECHConfig: *public_name* the name the server\n # answers for in the clear and has a certificate for (section 6.1.7\n # holds it to a host name); *config_id* random unless given - one the\n # server's other keys do not have (section 4.1); the KEM's key pair from\n # `Random::Secure`; the KDF and AEAD pairs a client may choose among, in\n # the server's order - HKDF-SHA256 with AES-128-GCM, then with\n # ChaCha20-Poly1305, unless given; and *maximum_name_length*, the\n # longest name it serves, which clients pad the name they ask for to\n # (section 6.1.3; 0 when not known). Its `config.raw` and\n # `private_key.bytes` are what `from_bytes` reads back.\n def self.generate(public_name : String, config_id : Int32 | ::Nil = nil, kem : HpkeKem = HpkeKem::X25519Sha256, suites : Array(::Tuple(HpkeKdf, HpkeAead)) = [{HpkeKdf::HkdfSha256, HpkeAead::Aes128Gcm}, {HpkeKdf::HkdfSha256, HpkeAead::ChaCha20Poly1305}], maximum_name_length : Int32 = 0) : TlsEchKey | TlsError\n def initialize(config : TlsEchConfig, private_key : HpkePrivateKey)\n def private_key : HpkePrivateKey\n # Whether the config goes in retry_configs, to a client whose encrypted\n # ClientHello this server turns down (section 7.1): true unless set; a\n # key kept only to open what holders of its old config still send is\n # set false, so that no client is told to retry with it.\n def retry : Bool\n # Whether the config goes in retry_configs, to a client whose encrypted\n # ClientHello this server turns down (section 7.1): true unless set; a\n # key kept only to open what holders of its old config still send is\n # set false, so that no client is told to retry with it.\n def retry=(value : Bool) : Bool\nend\n\n# What a connection answers when it cannot be made or kept, or a read that\n# came back with nothing yet.\npub struct TlsError\n # The alert this refusal sends the peer (`TlsAlert`), or nil: none for a\n # failure of the socket, or for an alert the peer sent.\n def alert : UInt8 | ::Nil\n def cancelled? : Bool\n # *message*, the alert's name after it when there is one: the sentence\n # shows the alert, and the alert is the field, not the sentence.\n def initialize(message : String, alert : UInt8 | ::Nil = nil, timed_out : Bool = false, cancelled : Bool = false, retry_configs : Array(UInt8) | ::Nil = nil)\n # The ECHConfigList a server that turned down this client's encrypted\n # ClientHello sent to retry with (RFC 9849 section 6.1.6), once its\n # certificate was checked for the config's public_name: on an\n # ech_required refusal, and nil on any other - and on an ech_required\n # refusal whose server sent none, which turned ECH off for it.\n def retry_configs : Array(UInt8) | ::Nil\n # A read that waited past its timeout, or that was cancelled: nothing was\n # lost and the connection is open - read again. Any other error ended it.\n def timed_out? : Bool\n def to_s : String\nend\n\n# A `TlsServerConfig` frozen (`TlsServerConfig#frozen`): what a server\n# whose connections run on several threads at once - std/http's\n# `Server.serve(tls:)` - reads on every worker. It is `Share`\n# (SPEC.md III.4.4), trusted rather than derived, and this is why it may\n# be. It holds a copy of the config it was made from (`dup`), which\n# nothing else holds and nothing writes after the constructor made it:\n# no method here assigns it or calls a setter on it, and it leaves only\n# for the `TlsServer` a handshake runs (`server`), which reads it and\n# has no getter for it. What the copy holds is `Share` itself or read\n# alone: its identities are `Share`, its ECH keys copied with it, its\n# arrays its own; the ticket keys and the replay guard it shares with\n# the config it was made from are `Share` by their fields, a `Mutex`\n# each, so a ticket one worker seals another opens and a replay one\n# refuses all refuse. Its identity source is a `SharedProc`, whose\n# block the compiler checked where it was written. The readers below\n# answer copies, a `List` or a new array, never the copy's own. What\n# changes while a server runs: an OCSP staple, through `staple`; the\n# ticket key, as it turns over; and whatever the identity source\n# answers, a certificate renewed (std/autocert). The rest - the\n# identities, the protocols, the ECH keys - is the server's for its\n# life: another set is another frozen config, served by another\n# `Server.serve`.\npub class TlsFrozenServerConfig\n def alpn : List(String)\n # `TlsServerConfig#ech_config_list`'s, of the keys frozen with it.\n def ech_config_list : Array(UInt8) | ::Nil\n def identity : TlsIdentity\n # *config* copied, its `identity_source` replaced by *source*. A config\n # with an `identity_source` and no *source* is refused by panicking, as\n # `TlsServerConfig#frozen` says.\n def initialize(config : TlsServerConfig, source : SharedProc(::Tuple(String | ::Nil, Array(String) | ::Nil, Int32 | ::Nil), TlsIdentity | TlsError | Nil) | ::Nil)\n def others : List(TlsIdentity)\n def replay_guard : TlsReplayGuard\n # The server's half of a connection on *socket* under this config, for\n # `TlsServer.over` to run the handshake of.\n def server(socket : IyiSocket) : TlsServer\n # *der* stapled for *identity*, as `TlsServerConfig#staple` does, from\n # any thread: the handshakes after it, on every worker, staple it.\n def staple(der : Array(UInt8), identity : TlsIdentity = @settings.identity) : TlsError | ::Nil\n def ticket_keys : TlsTicketKeys\nend\n\n# One ticket a client holds: the server's bytes, and the PSK, suite,\n# arrival (monotonic ms), lifetime, age_add, host, port, configuration,\n# protocol and certificates of the connection it came on, when the full\n# handshake that checked those certificates was made (monotonic ms; a\n# resumption's tickets carry the first one's), and the most early data it\n# allows (0 for none).\npub class TlsHeldTicket\n def age_add : Int64\n def alpn : String | ::Nil\n def authenticated_ms : Int64\n def host : String\n def initialize(ticket : Array(UInt8), psk : Array(UInt8), suite : Int32, received_ms : Int64, lifetime_ms : Int64, age_add : Int64, host : String, port : Int32, under : TlsMadeUnder, alpn : String | ::Nil, peer_certificates : Array(Certificate), authenticated_ms : Int64, max_early_data : Int64 = 0_i64)\n def lifetime_ms : Int64\n def max_early_data : Int64\n def peer_certificates : Array(Certificate)\n def port : Int32\n def psk : Array(UInt8)\n def received_ms : Int64\n def suite : Int32\n def ticket : Array(UInt8)\n def under : TlsMadeUnder\nend\n\n# A certificate chain, leaf first, and the key that signs for it -\n# Ed25519, ECDSA on P-256 or P-384 (std/p256, std/p384) or RSA (std/rsa),\n# each in constant time: what a server presents, and what a client\n# presents when a server asks.\n#\n# `Share` (SPEC.md III.4.4), trusted rather than derived: a server's\n# workers sign with one identity at once, and an `identity_source` that\n# runs on every worker answers identities it keeps. What the structural\n# rule cannot see is that nothing writes one after it is made: no method\n# here assigns a field, and none of std/x509's `Certificate`, std/p256's,\n# std/p384's and std/rsa's private keys does - a signature is computed\n# into new values, nothing is cached on the key - so what fails the rule\n# is only that the chain and the certificate are held in `Array`s. The\n# constructor copies the arrays it is given, so a caller that keeps them\n# writes its own. The one part that changes while a server runs, the OCSP\n# response it staples, is behind a `Mutex` (`staple`), which every\n# worker reads through. The edge is the one `List` closes by never\n# lending its array and this cannot: `chain`, `schemes` and the leaf's\n# fields are lent as arrays, read by every method of std, and a program\n# that writes into one writes what other threads read.\npub class TlsIdentity\n # The ClientCertificateType a TLS 1.2 CertificateRequest must list for\n # this key: rsa_sign (1), or ecdsa_sign (64) for P-256, P-384 and, by RFC\n # 8422 section 5.5, Ed25519.\n def certificate_type : UInt8\n # The chain as sent, leaf first, each certificate's DER.\n def chain : Array(Array(UInt8))\n # *chain*'s certificates, leaf first, and the 32-byte Ed25519 *seed*\n # that is the leaf's private key.\n def self.from_der(chain : Array(Array(UInt8)), seed : Array(UInt8)) : TlsIdentity | TlsError\n # *chain*'s certificates, leaf first, and the P-256 *key* that is the\n # leaf's.\n def self.from_der(chain : Array(Array(UInt8)), key : P256PrivateKey) : TlsIdentity | TlsError\n # *chain*'s certificates, leaf first, and the P-384 *key* that is the\n # leaf's.\n def self.from_der(chain : Array(Array(UInt8)), key : P384PrivateKey) : TlsIdentity | TlsError\n # *chain*'s certificates, leaf first, and the RSA *key* that is the\n # leaf's (2048 bits or more, std/rsa's floor for signing).\n def self.from_der(chain : Array(Array(UInt8)), key : RsaPrivateKey) : TlsIdentity | TlsError\n # The chain in *certificates*' CERTIFICATE blocks, leaf first, and the\n # leaf's private key in *private_key*: a PRIVATE KEY block (PKCS #8, an\n # Ed25519 key as RFC 8410 section 7 writes it, a P-256 or P-384 one as\n # RFC 5915 does, or an RSA one), an EC PRIVATE KEY block (SEC 1) or an\n # RSA PRIVATE KEY block (PKCS #1) - or why they are not a certificate\n # and key.\n def self.from_pem(certificates : String, private_key : String) : TlsIdentity | TlsError\n def initialize(chain : Array(Array(UInt8)), leaf : Certificate, schemes : Array(Int32), seed : Array(UInt8) | ::Nil, p256 : P256PrivateKey | ::Nil, p384 : P384PrivateKey | ::Nil, rsa : RsaPrivateKey | ::Nil)\n def leaf : Certificate\n # The OCSP response kept for this chain (`staple`), its DER; nil when\n # none is.\n def ocsp_staple : Array(UInt8) | ::Nil\n # Whether the key is RSA's: a TLS 1.2 server's ECDHE_RSA suites, and not\n # ECDHE_ECDSA's.\n def rsa? : Bool\n # The scheme a TLS 1.2 ServerKeyExchange is signed under for a client\n # that takes *offered*: `scheme_for`'s - for an RSA key, RSA-PSS by the\n # client's order - and for an RSA key whose client takes no RSA-PSS,\n # PKCS #1 v1.5 by its order, which 1.2 permits and which the clients that\n # never moved past it may take alone; nil when the key makes none.\n def scheme12_for(offered : Array(Int32)) : Int32 | ::Nil\n # The scheme to sign under for a peer that takes *offered* (its\n # signature_algorithms, in its order): the first of them this key makes,\n # or nil when it makes none.\n def scheme_for(offered : Array(Int32)) : Int32 | ::Nil\n # A scheme by the name RFC 8446 gives it.\n def self.scheme_name(scheme : Int32) : String\n # Schemes by name, for a sentence: \"a, b or c\".\n def self.scheme_names(schemes : Array(Int32)) : String\n # The schemes CertificateVerify can be signed under, by the key's kind:\n # ed25519 (0x0807), ecdsa_secp256r1_sha256 (0x0403),\n # ecdsa_secp384r1_sha384 (0x0503), or for an RSA key rsa_pss_rsae_sha256,\n # -384 and -512 (0x0804 to 0x0806). PKCS #1 v1.5 is never one: RFC 8446\n # section 4.2.3 keeps it out of CertificateVerify.\n def schemes : Array(Int32)\n # The leaf key's signature of *content* under *scheme*, one of\n # `schemes` - or for an RSA key in TLS 1.2, PKCS #1 v1.5's: what\n # CertificateVerify and a ServerKeyExchange carry.\n def sign(scheme : Int32, content : Array(UInt8)) : Array(UInt8)\n # *der* kept as this chain's OCSP response, to be stapled for a client\n # that asks: refused unless it is an OCSPResponse std/ocsp reads, with a\n # SingleResponse for the leaf - its serial number and its issuer name's\n # hash. Its signature and times are the client's to check; this server\n # only leaves one past its nextUpdate unsent. A later call replaces it,\n # from any thread, and a handshake on any worker staples the one held\n # when it asks.\n def staple(der : Array(UInt8)) : TlsError | ::Nil\n # The response to staple at *now*, with what it says; nil when there is\n # none, or it is past its nextUpdate.\n def staple_at(now : Time) : ::Tuple(Array(UInt8), OcspResponse, OcspSingleResponse) | ::Nil\nend\n\n# The key schedule of one connection (RFC 8446 section 7.1), over the\n# cipher suite's hash: SHA-384 for TLS_AES_256_GCM_SHA384, SHA-256 for the\n# other two.\npub class TlsKeySchedule\n # A PSK binder (section 4.2.11.2): a Finished keyed by the resumption's\n # binder key, over *transcript* - the messages before the ClientHello and\n # the ClientHello up to its binders.\n def binder(psk : Array(UInt8), transcript : Array(UInt8)) : Array(UInt8)\n # Derive-Secret over the messages so far.\n def derive_secret(secret : Array(UInt8), label : String, transcript : Array(UInt8)) : Array(UInt8)\n # The early secret of a resumption, from its PSK.\n def early_secret(psk : Array(UInt8)) : Array(UInt8)\n # The early secret with no PSK, and the \"derived\" salt for the next stage.\n def early_secret : Array(UInt8)\n # The client's early traffic secret (section 7.1), over the ClientHello\n # alone: what 0-RTT data and EndOfEarlyData go under.\n def early_traffic(psk : Array(UInt8), hello : Array(UInt8)) : Array(UInt8)\n # HKDF-Expand-Label: the label prefixed \"tls13 \", and the context, in an\n # HkdfLabel.\n def expand_label(secret : Array(UInt8), label : String, context : Array(UInt8), length : Int32) : Array(UInt8)\n def extract(salt : Array(UInt8), ikm : Array(UInt8)) : Array(UInt8)\n # A Finished message's verify_data under *secret* (section 4.4.4).\n def finished(secret : Array(UInt8), transcript : Array(UInt8)) : Array(UInt8)\n # The handshake secret over the key exchange's *shared* secret, and over\n # *psk* in a resumption.\n def handshake_secret(shared : Array(UInt8), psk : Array(UInt8) | ::Nil = nil) : Array(UInt8)\n def hash(data : Array(UInt8)) : Array(UInt8)\n def hash_size : Int32\n def hmac(key : Array(UInt8), data : Array(UInt8)) : Array(UInt8)\n def initialize(sha384 : Bool)\n def master_secret(handshake_secret : Array(UInt8)) : Array(UInt8)\n # The next generation of a traffic secret (section 7.2).\n def next_secret(secret : Array(UInt8)) : Array(UInt8)\n # TLS 1.2's PRF (RFC 5246 section 5): P_hash over this schedule's hash -\n # SHA-256, or SHA-384 for a suite that names it - keyed by *secret*, over\n # *label* and *seed*, *length* bytes of it.\n def prf(secret : Array(UInt8), label : String, seed : Array(UInt8), length : Int32) : Array(UInt8)\n # The PSK a NewSessionTicket's *nonce* makes of the resumption master\n # secret (section 4.6.1): one ticket's, and no other's.\n def resumption_psk(secret : Array(UInt8), nonce : Array(UInt8)) : Array(UInt8)\n def zeros : Array(UInt8)\nend\n\n# The `TlsConfig` a client connection was made under, and the parts of it\n# that decide whom the connection trusts, what it presented and whether\n# it named its server in the clear: the roots, the identity, the time\n# certificates were checked at, the oldest version spoken, whether an\n# OCSP staple was required, and the ECHConfigList it was encrypted under.\n# Whatever was made under it - a `TlsSession`'s ticket, std/http's kept\n# connection - is used again only under a configuration that is the same\n# object holding the same ones, so what one trust store, identity or\n# staple rule checked is never carried under another: set by assigning a\n# new configuration, or by assigning `roots`, `identity`, `now`,\n# `min_version`, `require_staple` or `ech_configs` on it in place. Objects\n# are told apart by `object_id`, and the references are held, so an\n# address freed and handed to a new configuration is never taken for the\n# old one. Certificates added to the same roots array in place are not\n# seen: such an array is still the same object. `ech_configs` is held to\n# its bytes, kept as they were: a connection made in the clear is not\n# handed a request made under a configuration that asks for ECH, nor its\n# ticket offered inside one, and a ticket that came inside one is not\n# offered in a ClientHello that names the server in the clear, where it\n# would tie the two connections together for anyone watching (RFC 9849\n# section 6.1.8 keeps ECH state to the source it came from). One\n# definition serves std/tls and std/http, so the two cannot come to\n# compare different parts.\npub class TlsMadeUnder\n def initialize(config : TlsConfig)\n def same?(config : TlsConfig) : Bool\nend\n\n# One direction's record protection (section 5.2): the AEAD keyed from a\n# traffic secret, and the record sequence number its nonces are made of -\n# or TLS 1.2's, keyed from its key block.\npub class TlsRecordCipher\n # TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256.\n def initialize(suite : Int32, schedule : TlsKeySchedule, secret : Array(UInt8))\n # TLS 1.2's (RFC 5246 section 6.2.3.3) for one of its AEAD suites, *key*\n # and *iv* from the key block: AES-GCM's four-byte salt, to which each\n # record's eight-byte explicit nonce is added (RFC 5288 section 3), or\n # ChaCha20-Poly1305's twelve bytes, XORed with the sequence number as TLS\n # 1.3 does it (RFC 7905 section 2).\n def initialize(suite : Int32, key : Array(UInt8), iv : Array(UInt8))\n def key_iv : Array(UInt8)\n # The type and content of a protected record whose 5-byte header is\n # *header* and body *body*.\n def open(header : Array(UInt8), body : Array(UInt8)) : ::Tuple(UInt8, Array(UInt8)) | TlsError\n # The whole record that carries *content* of *type*, protected.\n def seal(type : UInt8, content : Array(UInt8)) : Array(UInt8)\n def sequence : UInt64\nend\n\n# What keeps a server from taking the same early data twice (RFC 8446\n# sections 8.2 and 8.3). A ClientHello's early data is taken only if it is\n# fresh - the ticket age the client gives is within `WINDOW_MS` of the\n# time this server counts since it issued the ticket - and only once:\n# each ClientHello whose early data is taken is recorded by its PSK\n# binder, which nobody without the ticket's PSK can make for another\n# ClientHello, and a ClientHello whose binder is recorded has its early\n# data refused (its handshake goes on, the data then sent after it). A\n# replay that is still fresh comes within two windows of its original, so\n# a binder is held that long and then dropped; with `capacity` held, early\n# data is refused until some are - the guard fails closed. A refusal costs\n# the client a round trip, never its request.\n#\n# What it does not do. It holds in this process, as the ticket keys do, so\n# a ticket - and the early data under it - is taken by no other server\n# and by no later run of this one; servers that shared ticket keys would\n# need to share this too, and neither is offered. It cannot stop a client\n# from sending the same request again after a connection whose early data\n# was taken broke before the answer came (appendix E.5), so what comes\n# early must be safe to run twice; and taking early data at all lets\n# whoever recorded the ClientHello replay it within the window to a server\n# that has not seen it - none, here.\n#\n# The binders are behind a `Mutex`, as the ticket keys are, so every\n# worker of a `TlsFrozenServerConfig`'s server records into one guard and\n# a replay sent to another worker is refused there too.\npub class TlsReplayGuard\n # Whether early data may be taken, at *now_ms* (monotonic), for a\n # ClientHello whose PSK binder is *binder*: not one held already, and not\n # past `capacity`. It is recorded when it may; a task cancelled while it\n # waits for the guard is refused.\n def admit(binder : Array(UInt8), now_ms : Int64) : Bool\n def capacity : Int32\n # Whether a ticket age the client gives, *client_ms*, and this server's\n # count since it issued the ticket, *server_ms*, are within the window.\n def self.fresh?(client_ms : Int64, server_ms : Int64) : Bool\n def initialize(capacity : Int32 = 65536)\n # How many binders are held.\n def size : Int32\nend\n\n# A connection from a client, its handshake done: TLS 1.3 as a server -\n# or 1.2, when its config's `min_version` asks and the client speaks\n# nothing newer - its certificate's key (Ed25519, P-256, P-384 or RSA)\n# signing for it.\npub class TlsServer < TlsConnection\n # Whether the client's early data was taken (0-RTT): `read_early` reads\n # it, and `read` never does.\n def early_data? : Bool\n # The rest of a handshake whose early data was taken: the early data not\n # yet read, kept for `read_early`, then EndOfEarlyData and the client's\n # Finished. Nil once the handshake is done, at once when nothing was\n # early. `read` calls it first; a server that answers early data before\n # the client's Finished calls it before it closes.\n def finish : TlsError | ::Nil\n # The handshake, or why it failed - the client sent that alert first.\n def handshake : TlsError | ::Nil\n # The certificate and key this connection was shown under: the config's\n # `identity`, or one of its `others` the client asked for by name.\n def identity : TlsIdentity\n def initialize(socket : IyiSocket, config : TlsServerConfig, shared_source : SharedProc(::Tuple(String | ::Nil, Array(String) | ::Nil, Int32 | ::Nil), TlsIdentity | TlsError | Nil) | ::Nil = nil)\n # The ClientHelloOuter's server_name when its encrypted ClientHelloInner\n # was taken: the public_name the client named in the clear, where\n # `server_name` is the name it asked for inside. Nil otherwise.\n def outer_server_name : String | ::Nil\n # The handshake with the client on *socket*. A handshake that fails\n # sends the client the alert its error names, and the socket is closed.\n # Where the config allows early data (`max_early_data`) and *early_data*\n # is true, a ClientHello's early data may be taken: then the handshake\n # answers once this side's Finished is sent, `read_early` reads the\n # early data, a write goes at once (0.5-RTT), and `finish` - which\n # `read` calls - reads the client's Finished. Under a frozen config\n # (`TlsFrozenServerConfig`) the handshake may run on any thread, as many\n # at once as there are.\n def self.over(socket : IyiSocket, config : TlsServerConfig | TlsFrozenServerConfig, timeout_ms : Int32 = HANDSHAKE_TIMEOUT_MS, early_data : Bool = true) : TlsServer | TlsError\n # Up to *max* bytes the client sent after the handshake - never its early\n # data, which `read_early` reads - the handshake finished first.\n def read(max : Int32 = 16384) : String | TlsError\n # Up to *max* bytes of the early data the client sent with its\n # ClientHello, \"\" once it has all been read, or when none was taken.\n # Early data may be a replay (RFC 8446 section 8; `TlsReplayGuard` says\n # what this server refuses and what it cannot), which is why it is read\n # here and never by `read`: a program can tell it from what came after\n # the handshake, and act on it only where twice is safe.\n def read_early(max : Int32 = 16384) : String | TlsError\n # The host the client named (server_name, RFC 6066), if it named one.\n def server_name : String | ::Nil\n def server_side? : Bool\n protected def takes_early=(takes : Bool) : Bool\nend\n\n# What a server presents and asks: its identity, the protocols it speaks\n# by ALPN, the oldest version it speaks, and - for mutual TLS - the roots\n# a client's certificate must chain to.\n#\n# A config is written as a server runs - its properties are assigned, an\n# identity is added - and its `identity_source` is a `Proc`, which may\n# have captured anything; so it is not `Share` (SPEC.md III.4.4), and a\n# server whose connections run on several threads at once (std/http's\n# `Server.serve`) takes `frozen` instead: a copy no property of\n# which changes, that every worker reads at once. What must change while\n# such a server runs is shared rather than copied, and synchronised: the\n# ticket keys and the replay guard, whose state is behind a `Mutex`, so\n# a ticket one worker seals another opens; and each identity's OCSP\n# staple (`staple`), which a fresh response replaces from any thread.\npub class TlsServerConfig\n # *identity* shown to a client asking for a host its certificate names.\n def add_identity(identity : TlsIdentity) : TlsServerConfig\n # Protocols this server speaks by ALPN (RFC 7301), most wanted first; a\n # client that offers some and none of these is refused. None when empty.\n def alpn : Array(String)\n # Protocols this server speaks by ALPN (RFC 7301), most wanted first; a\n # client that offers some and none of these is refused. None when empty.\n def alpn=(value : Array(String)) : Array(String)\n def chain : Array(Array(UInt8))\n def client_required : Bool\n def client_required=(value : Bool) : Bool\n # When set, the server asks for a client's certificate (a\n # CertificateRequest) and checks it as std/x509 checks a client's\n # (clientAuth) against these roots, then its CertificateVerify; a client\n # that sends none is refused (certificate_required; in TLS 1.2,\n # handshake_failure) while `client_required`. Not asked when nil.\n def client_roots : Array(Certificate) | ::Nil\n # When set, the server asks for a client's certificate (a\n # CertificateRequest) and checks it as std/x509 checks a client's\n # (clientAuth) against these roots, then its CertificateVerify; a client\n # that sends none is refused (certificate_required; in TLS 1.2,\n # handshake_failure) while `client_required`. Not asked when nil.\n def client_roots=(value : Array(Certificate) | ::Nil) : Array(Certificate) | ::Nil\n # A copy, to be written apart from this one: the arrays copied - the\n # protocols, roots, identities, groups, and each ECH key, whose `retry`\n # is its own - and the ticket keys and the replay guard shared, so that\n # a ticket either seals the other opens, and early data either took\n # the other refuses.\n def dup : TlsServerConfig\n # `max_early_data`, held to 0 .. `MAX_EARLY_DATA`.\n def early_limit : Int64\n # The ECHConfigList of the `ech_keys` whose `retry` is set, in their\n # order: what this server publishes for clients (a DNS HTTPS record's\n # `ech`) and sends as retry_configs; nil when there are none.\n def ech_config_list : Array(UInt8) | ::Nil\n # The keys an encrypted ClientHello (RFC 9849) is opened with, as a\n # client-facing server in shared mode, which is its own backend: one\n # whose ECHClientHello names a key's config_id and suite and opens under\n # it is answered on its ClientHelloInner; one none opens, on the\n # ClientHelloOuter, for the public_name, with `ech_config_list` as\n # retry_configs. Several for rotation (`TlsEchKey`). None when empty: an\n # encrypted_client_hello is then passed over, unread past its type.\n def ech_keys : Array(TlsEchKey)\n # The keys an encrypted ClientHello (RFC 9849) is opened with, as a\n # client-facing server in shared mode, which is its own backend: one\n # whose ECHClientHello names a key's config_id and suite and opens under\n # it is answered on its ClientHelloInner; one none opens, on the\n # ClientHelloOuter, for the public_name, with `ech_config_list` as\n # retry_configs. Several for rotation (`TlsEchKey`). None when empty: an\n # encrypted_client_hello is then passed over, unread past its type.\n def ech_keys=(value : Array(TlsEchKey)) : Array(TlsEchKey)\n def self.from_der(chain : Array(Array(UInt8)), seed : Array(UInt8)) : TlsServerConfig | TlsError\n def self.from_der(chain : Array(Array(UInt8)), key : P256PrivateKey) : TlsServerConfig | TlsError\n def self.from_der(chain : Array(Array(UInt8)), key : P384PrivateKey) : TlsServerConfig | TlsError\n def self.from_der(chain : Array(Array(UInt8)), key : RsaPrivateKey) : TlsServerConfig | TlsError\n # `TlsIdentity.from_pem`'s identity, as a server's.\n def self.from_pem(certificates : String, private_key : String) : TlsServerConfig | TlsError\n # This config as it is now, for a server whose connections run on\n # several threads at once (`TlsFrozenServerConfig`). One with an\n # `identity_source` is refused by panicking: a `Proc` may have captured\n # anything, and nothing checked it - the source goes to the block form\n # below instead, where the compiler checks it.\n def frozen : TlsFrozenServerConfig\n # The same, with the block as its `identity_source`, asked with the\n # host, the protocols and the milliseconds left as that is. The def is\n # `@[Share]`, so the block is checked where it is written as a parallel\n # task's is (SPEC.md III.4.4, III.4.13): every value it captures must be\n # `Share` - identities, which are, a `Mutex` around a table of them, a\n # `Channel` to the thread that keeps them - every captured local\n # assigned once, and the constants and class variables it names\n # `Share`. It is called on whichever worker the handshake runs on,\n # several at once. `identity_source`, if set, is not asked.\n def frozen(&source : (String | ::Nil, Array(String) | ::Nil, Int32 | ::Nil -> TlsIdentity | TlsError | Nil)) : TlsFrozenServerConfig\n # `groups`' code points, those this module has, once each.\n def group_codes : Array(Int32)\n # The key exchange groups a TLS 1.3 handshake takes, by the names\n # `named_group` gives them, most wanted first: X25519MLKEM768, X25519,\n # secp256r1 and secp384r1 unless set. The first of them a client lists is\n # taken where it sent a share in it, or is X25519MLKEM768 - for which a\n # client that sent no share is asked by HelloRetryRequest - and the next\n # otherwise; a client that sent a share in none of them it lists is asked\n # for the first it lists. A name this module has no group of is passed\n # over. TLS 1.2's groups are its own, as the module's header says.\n def groups : Array(String)\n # The key exchange groups a TLS 1.3 handshake takes, by the names\n # `named_group` gives them, most wanted first: X25519MLKEM768, X25519,\n # secp256r1 and secp384r1 unless set. The first of them a client lists is\n # taken where it sent a share in it, or is X25519MLKEM768 - for which a\n # client that sent no share is asked by HelloRetryRequest - and the next\n # otherwise; a client that sent a share in none of them it lists is asked\n # for the first it lists. A name this module has no group of is passed\n # over. TLS 1.2's groups are its own, as the module's header says.\n def groups=(value : Array(String)) : Array(String)\n def identity : TlsIdentity\n # The identity at *index*, as `identity_index` counts; nil past them.\n def identity_at(index : Int32) : TlsIdentity | ::Nil\n # The identity a client asking for *name*, taking signatures under\n # *schemes*, is shown: the first of `others` that names the host and\n # signs under a scheme the client takes, else `identity` if it does; nil\n # when none of them does.\n def identity_for(name : String | ::Nil, schemes : Array(Int32)) : TlsIdentity | ::Nil\n # The place of *identity* among this server's, which a ticket carries:\n # 0 for `identity`, then `others` in order.\n def identity_index(identity : TlsIdentity) : Int32\n # Asked first, in TLS 1.3 and 1.2, for the identity a client is shown:\n # given the host the client named by server_name (nil when it named\n # none), the protocols its ALPN offered (nil when it offered none), and\n # the milliseconds left of the handshake's deadline (0 once it has\n # passed, nil when the handshake has none). An identity it answers is\n # shown where one of `others` would have been - so a scheme of its key\n # the client takes, and in 1.2 a suite and a curve for it, or the client\n # is refused as for `identity`; nil leaves the choice to `others` and\n # `identity` as without it; an error refuses the handshake, the client\n # sent the alert that error names. Asked of each ClientHello that lists\n # signature_algorithms - a second one after a HelloRetryRequest too, and\n # one offering a ticket, which may yet come to a full handshake; a\n # resumption shows no certificate, and its ticket counts the session as\n # `identity`'s (`identity_index`). The handshake waits for it, and its\n # deadline cuts only the reads after it: a source that may wait - for a\n # CA, say - keeps its waits within the time it is given, or holds the\n # connection past the deadline. Not asked when nil.\n def identity_source : Proc(String | ::Nil, Array(String) | ::Nil, Int32 | ::Nil, TlsIdentity | TlsError | Nil) | ::Nil\n # Asked first, in TLS 1.3 and 1.2, for the identity a client is shown:\n # given the host the client named by server_name (nil when it named\n # none), the protocols its ALPN offered (nil when it offered none), and\n # the milliseconds left of the handshake's deadline (0 once it has\n # passed, nil when the handshake has none). An identity it answers is\n # shown where one of `others` would have been - so a scheme of its key\n # the client takes, and in 1.2 a suite and a curve for it, or the client\n # is refused as for `identity`; nil leaves the choice to `others` and\n # `identity` as without it; an error refuses the handshake, the client\n # sent the alert that error names. Asked of each ClientHello that lists\n # signature_algorithms - a second one after a HelloRetryRequest too, and\n # one offering a ticket, which may yet come to a full handshake; a\n # resumption shows no certificate, and its ticket counts the session as\n # `identity`'s (`identity_index`). The handshake waits for it, and its\n # deadline cuts only the reads after it: a source that may wait - for a\n # CA, say - keeps its waits within the time it is given, or holds the\n # connection past the deadline. Not asked when nil.\n def identity_source=(value : Proc(String | ::Nil, Array(String) | ::Nil, Int32 | ::Nil, TlsIdentity | TlsError | Nil) | ::Nil) : Proc(String | ::Nil, Array(String) | ::Nil, Int32 | ::Nil, TlsIdentity | TlsError | Nil) | ::Nil\n def initialize(identity : TlsIdentity)\n def leaf : Certificate\n # The ticket lifetime in milliseconds, held to 1 s .. seven days.\n def lifetime_ms : Int64\n # The most early data (0-RTT, RFC 8446 section 2.3) a ticket lets a\n # client send with its ClientHello, which `TlsServer#read_early` reads\n # apart from what `read` does: none when 0, as unless set, and at most\n # `MAX_EARLY_DATA`. Early data can be replayed: `replay_guard` refuses\n # what it can (see `TlsReplayGuard` for what it does not), and what\n # comes early must still be safe to act on twice.\n def max_early_data : Int32\n # The most early data (0-RTT, RFC 8446 section 2.3) a ticket lets a\n # client send with its ClientHello, which `TlsServer#read_early` reads\n # apart from what `read` does: none when 0, as unless set, and at most\n # `MAX_EARLY_DATA`. Early data can be replayed: `replay_guard` refuses\n # what it can (see `TlsReplayGuard` for what it does not), and what\n # comes early must still be safe to act on twice.\n def max_early_data=(value : Int32) : Int32\n # The oldest version this server speaks: TLS 1.3 by default, and then a\n # client that does not offer it is refused (protocol_version); `TLS12`\n # answers a client that offers 1.2 and not 1.3 in 1.2, held to what the\n # module's header says. A client that offers 1.3 is answered in it\n # either way.\n def min_version : TlsVersion\n # The oldest version this server speaks: TLS 1.3 by default, and then a\n # client that does not offer it is refused (protocol_version); `TLS12`\n # answers a client that offers 1.2 and not 1.3 in 1.2, held to what the\n # module's header says. A client that offers 1.3 is answered in it\n # either way.\n def min_version=(value : TlsVersion) : TlsVersion\n # The time a client's certificate is checked at; now when nil.\n def now : Time | ::Nil\n # The time a client's certificate is checked at; now when nil.\n def now=(value : Time | ::Nil) : Time | ::Nil\n # More identities, each shown to a client that asks by server_name (RFC\n # 6066) for a host its certificate names; `identity` to any other.\n def others : Array(TlsIdentity)\n # What refuses early data a server has taken before (RFC 8446 section 8).\n def replay_guard : TlsReplayGuard\n def schemes : Array(Int32)\n protected def share_state(keys : TlsTicketKeys, guard : TlsReplayGuard) : Nil\n # *der*, an OCSP response for *identity*'s leaf, stapled to its\n # certificate for a client that asks (RFC 6066's status_request; in TLS\n # 1.3 the leaf's CertificateEntry carries it, in 1.2 a CertificateStatus):\n # see `TlsIdentity#staple` for what is refused. A later call replaces it,\n # as a server fetching fresh responses would.\n def staple(der : Array(UInt8), identity : TlsIdentity = @identity) : TlsError | ::Nil\n # The keys this server's tickets are sealed under.\n def ticket_keys : TlsTicketKeys\n # How long a ticket may be resumed by, in seconds, from 1 to seven days\n # (604800, section 4.6.1's most); the period the ticket key turns over\n # in, too (`TlsTicketKeys`).\n def ticket_lifetime : Int32\n # How long a ticket may be resumed by, in seconds, from 1 to seven days\n # (604800, section 4.6.1's most); the period the ticket key turns over\n # in, too (`TlsTicketKeys`).\n def ticket_lifetime=(value : Int32) : Int32\n # NewSessionTickets sent after each handshake, for a client to resume\n # by (RFC 8446 section 4.6.1); none when 0, and at most `MAX_TICKETS`.\n def tickets : Int32\n # NewSessionTickets sent after each handshake, for a client to resume\n # by (RFC 8446 section 4.6.1); none when 0, and at most `MAX_TICKETS`.\n def tickets=(value : Int32) : Int32\nend\n\n# What a client keeps to resume a server's sessions (RFC 8446 section\n# 2.2): the tickets its NewSessionTickets carry, each with the PSK its\n# nonce makes. The caller holds it and hands it to `TlsClient.connect` or\n# `over`; there is no store of the process's own. A connection offers the\n# newest ticket that fits it, and takes it out, for a ticket goes out once\n# (appendix C.4): two resumptions are not tied together by it. A resumed\n# handshake checks no certificate - the ticket stands for the check the\n# first one made, and its chain is the resumed connection's\n# `peer_certificates` - so a ticket fits only a connection that check\n# would stand for:\n# - to the host and port it came from: section 4.6.1 has a client resume\n# only for a name the first handshake's certificate was checked for, and\n# a ticket offered to another port names the client to whatever server\n# listens there;\n# - under the configuration it came under (`TlsMadeUnder`): one session\n# shared by two configurations resumed the first one's ticket under the\n# second, its roots, `now` and `require_staple` never applied, and with\n# mutual TLS the server took the ticket's client certificate for the\n# connection's, so a connection made under identity B was identity A's;\n# - while the server's certificate is valid at the configuration's `now`\n# (the clock's when nil), as Go's client has it: a session past its\n# leaf's notAfter is a full handshake, which refuses it;\n# - within its lifetime, and within seven days (`MAX_LIFETIME`, section\n# 4.6.1's most) of the full handshake that checked those certificates,\n# however many resumptions came between: a server that sent fresh\n# tickets on every resumed connection otherwise kept the session alive\n# for as long as the client came back.\npub class TlsSession\n # A session holding at most *limit* tickets, held to 0 ..\n # `MAX_TICKETS`: std/http's `HTTPClient` keeps fewer for each origin.\n def initialize(limit : Int32 = MAX_TICKETS)\n # *ticket* held, the oldest gone past the session's limit.\n def keep(ticket : TlsHeldTicket) : Nil\n # How many tickets are held.\n def size : Int32\n # The newest ticket for *host* and *port* under *config* (the class's\n # header says which fit), taken out, at *now_ms* by the monotonic clock.\n # Those past their lifetimes or seven days from their full handshake go\n # too, and so does one that fits but whose server certificate has\n # expired.\n def take(host : String, port : Int32, config : TlsConfig, now_ms : Int64) : TlsHeldTicket | ::Nil\nend\n\n# The session a server's ticket seals, for a resumption to take up: the\n# suite, when it was issued (monotonic ms) and for how long (seconds),\n# its age_add, the PSK, the name the client asked for, the protocol\n# chosen, the identity shown (`TlsServerConfig#identity_index`), the\n# client's certificates (DER, leaf first), and when the first handshake\n# checked the certificates the session rests on (monotonic ms). The name's\n# length is two bytes and the identity's place four: in version 1 each\n# was one, and a name past 255 bytes, which the ClientHello then let\n# through, sealed a ticket that never opened, and a place past 255 among\n# `others` wrapped round. The PSK (48 bytes at most), the protocol (one\n# byte long in ALPN itself) and the chain's count - a chain of 256\n# certificates is far past `TlsServerConfig::MAX_TICKET`, which no ticket\n# is sent beyond - keep one byte.\npub class TlsTicket\n def age_add : Int64\n def alpn : String | ::Nil\n def authenticated_ms : Int64\n def chain : Array(Array(UInt8))\n # The state *bytes* hold, or nil when they are not one this encodes.\n def self.decode(bytes : Array(UInt8)) : TlsTicket | ::Nil\n def encode : Array(UInt8)\n def identity : Int32\n def initialize(suite : Int32, issued_ms : Int64, lifetime : Int64, age_add : Int64, psk : Array(UInt8), server_name : String | ::Nil, alpn : String | ::Nil, identity : Int32, chain : Array(Array(UInt8)), authenticated_ms : Int64, max_early_data : Int64 = 0_i64)\n def issued_ms : Int64\n def lifetime : Int64\n def max_early_data : Int64\n # The session *ticket* seals, opened by *keys* at *now_ms*: nil when no\n # key held opens it, or when its lifetime is over by this server's own\n # count from its issue.\n def self.open(keys : TlsTicketKeys, ticket : Array(UInt8), now_ms : Int64, period_ms : Int64) : TlsTicket | ::Nil\n def psk : Array(UInt8)\n def server_name : String | ::Nil\n def suite : Int32\nend\n\n# The keys a server seals its tickets under. RFC 8446 section 4.6.1 leaves\n# a ticket's form to the server; this one is RFC 5077 section 4's shape:\n# the key's 8-byte name, a 12-byte nonce drawn at random, and the state\n# sealed by ChaCha20-Poly1305 (std/aead, constant-time) with the name as\n# its additional data. A key made at random in this process seals for one\n# period - the ticket lifetime - and then only opens, for one more: a\n# ticket opens for all its lifetime, and no key opens anything past two\n# periods. A key idle past both goes with the one before it. The keys live\n# in this process: a ticket outlives neither a restart nor a move to\n# another server, which would need the keys shared and is not offered.\n#\n# Every thread of the process may seal and open at once - a server's\n# workers under `TlsFrozenServerConfig` share one set, so a ticket one\n# sealed another opens - so the keys are behind a `Mutex`, which makes\n# this type `Share` by its fields: the current name and key, and the\n# previous ones, are read under the lock, the key turned over there when\n# it is due, and the seal or open done outside it with the key read.\n# The lock is held for a comparison and four reads, and for two random\n# draws when the key turns over.\npub class TlsTicketKeys\n def initialize(now_ms : Int64 = TlsTime.now_ms)\n # The state *ticket* seals, when a key still held at *now_ms* opens it;\n # nil for any other bytes.\n def open(ticket : Array(UInt8), now_ms : Int64, period_ms : Int64) : Array(UInt8) | ::Nil\n # A new key from *now_ms*: what the one before sealed still opens, and\n # what the one before that sealed no longer does.\n def rotate(now_ms : Int64 = TlsTime.now_ms) : Nil\n # *state* sealed at *now_ms* (monotonic) under the current key - a new\n # one first, when *period_ms* has passed since it was made. Empty only\n # when the task was cancelled while it waited for the keys.\n def seal(state : Array(UInt8), now_ms : Int64, period_ms : Int64) : Array(UInt8)\nend\n\n# The versions a connection may speak: TLS 1.3, or TLS 1.2 as well (RFC\n# 5246), for a peer that never moved past it.\npub enum TlsVersion : Int32\n TLS12 = 0\n TLS13 = 1\nend\n\nimpl Error for TlsError\n def message : String\nend\n" }, { "name": "std/toml", "path": "src/std/toml.iyi", - "sourceSha256": "cbcee8fd6268a7d7bd2f67d2eea7165f659c33bd311d9f06c8c933a82a8d7bb7", + "sourceSha256": "4c062fc395c687687316c73840acec7db0a963e24e8aeddad4816c8900e70fa6", "surface": "module std/toml\n\n# `derive toml_serializable`, in a struct or class whose fields are written\n# with their types: the type reads and writes itself as a TOML table, a\n# key per field, named as the field is.\n#\n# pub struct Coord\n# getter x : Float64\n# getter name : String\n# derive toml_serializable\n# end\n#\n# Coord.from_toml(\"x = 1.5\\nname = \\\"a\\\"\\n\").x # => 1.5\n# coord.to_toml # => \"x = 1.5\\nname = \\\"a\\\"\\n\"\n#\n# It is std/yaml's `derive yaml_serializable` over TOML, field for field:\n# a value is read and written as `TOML.read_value` and `TOML.to_any` take\n# its type (Bool, Int32, Int64, UInt8, UInt64, Float64, String,\n# `TOMLDateTime`, `Any`, an `Array` or a `Hash(String, _)` of them,\n# another derived type, each of them or nil). TOML has no null, so a nil\n# field is not written, and a missing key is read as YAML's derive reads\n# one: a field with a default keeps it, a nilable field holds nil, and any\n# other is refused by name. A key the type has no field for is passed\n# over. A type that is several writes `derive serializable,\n# yaml_serializable, toml_serializable`: what this one generates shares no\n# method with theirs (`to_toml`, `from_toml`, `to_toml_any` and\n# `initialize(toml : Any)` of this module's `Any`).\npub macro toml_serializable(declaration)\n\n# The value a document holds. A document is a table: `TOML.parse` answers\n# an `Any` whose `raw` is a `Hash(String, Any)`.\npub struct Any\n alias Type = Bool | Int64 | Float64 | String | TOMLDateTime | Array(Any) | Hash(String, Any)\n def !=(other : Any) : Bool\n # Structural: same kind, same value; a table equals a table with the\n # same pairs in any order. `1` and `1.0` are different values, two NaNs\n # the same one, and `0.0` equals `-0.0` as the doubles do. Arrays and\n # tables are compared in methods of their own and scalars in a third,\n # so the frames a deep value recurses through stay small: with all of\n # it here, comparing two arrays nested 100 deep overflowed a task's\n # stack in a plain build.\n def ==(other : Any) : Bool\n def [](key : String) : Any\n def [](index : Int32) : Any\n def []?(key : String) : Any | ::Nil\n def []?(index : Int32) : Any | ::Nil\n def as_a : Array(Any)\n def as_a? : Array(Any) | ::Nil\n def as_bool : Bool\n def as_bool? : Bool | ::Nil\n def as_datetime : TOMLDateTime\n def as_datetime? : TOMLDateTime | ::Nil\n # A float, or an integer widened to one.\n def as_f : Float64\n def as_f? : Float64 | ::Nil\n def as_h : Hash(String, Any)\n def as_h? : Hash(String, Any) | ::Nil\n def as_i : Int64\n def as_i? : Int64 | ::Nil\n def as_s : String\n def as_s? : String | ::Nil\n def dig(key : String | Int32) : Any\n def dig(key : String | Int32, *rest : String | Int32) : Any\n def dig?(key : String | Int32) : Any | ::Nil\n def dig?(key : String | Int32, *rest : String | Int32) : Any | ::Nil\n # Agrees with `==`: a table's hash does not depend on its order, `0.0`\n # and `-0.0` share one and so do all NaNs. Mixed as std/yaml's `Any`\n # mixes, for the spread that file measured.\n def hash : Int32\n def initialize(raw : Type)\n def initialize(value : Int32)\n # A value as TOML writes it inline: strings quoted and escaped, floats\n # as `dump` spells them, tables as inline tables.\n def inspect : String\n # Which member of the union `raw` is, as one of the KIND_ constants.\n # Reference types are tested first, for the reason std/yaml's header\n # gives.\n def kind : Int32\n # The name of the kind, for the sentences below.\n def kind_name : String\n def raw : Type\n # Items of an array, keys of a table, characters of a string.\n def size : Int32\n # A string as itself, anything else as `inspect` writes it.\n def to_s : String\nend\n\npub struct TOML\n # TOML for a table, ending in a line break unless the table is empty.\n def self.dump(value : Any) : String\n # The document `text` holds, read into `type`: `Bool`, `Int32`, `Int64`,\n # `UInt8`, `UInt64`, `Float64`, `String`, `TOMLDateTime`, `Any`, a type\n # with `derive toml_serializable`, one of those or nil, or an `Array` or a\n # `Hash(String, _)` of those. Text that is not TOML panics with the\n # `TOMLError`'s sentence; a value of another kind, or an integer outside\n # the type, is refused.\n def self.from_toml(text : String, type : T.class) : T forall T\n def initialize\n # The document `text` holds, a table, or what is wrong with it.\n def self.parse(text : String) : Any | TOMLError\n # The document, or nil where `parse` answers a `TOMLError`.\n def self.parse?(text : String) : Any | ::Nil\n def self.read_value(value : Any, type : Bool.class) : Bool\n def self.read_value(value : Any, type : Int32.class) : Int32\n def self.read_value(value : Any, type : Int64.class) : Int64\n def self.read_value(value : Any, type : UInt8.class) : UInt8\n def self.read_value(value : Any, type : UInt64.class) : UInt64\n # A float, or an integer widened to one: `y = 2` is the Float64 2.0.\n def self.read_value(value : Any, type : Float64.class) : Float64\n def self.read_value(value : Any, type : String.class) : String\n def self.read_value(value : Any, type : TOMLDateTime.class) : TOMLDateTime\n def self.read_value(value : Any, type : Any.class) : Any\n def self.read_value(value : Any, type : Array(T).class) : Array(T) forall T\n def self.read_value(value : Any, type : Hash(String, V).class) : Hash(String, V) forall V\n # A type that reads itself, the `initialize(toml)` a `derive\n # toml_serializable` wrote; a type that admits nil reads as the other\n # type, since a value that is there is never null.\n def self.read_value(value : Any, type : T.class) : T forall T\n # The `Any` a value is written as. TOML has no null: a nil is refused\n # here, named, and a derived type leaves a nil field out before it gets\n # this far. Taken out before the rest dispatches, for the reason\n # std/json's `write_or_null` gives.\n def self.to_any(val : T) : Any forall T\n # TOML for any table `to_any` makes of `obj`, as `dump` writes it.\n def self.to_toml(obj : T) : String forall T\n def self.write_value(val : Bool) : Any\n def self.write_value(val : Int32) : Any\n def self.write_value(val : Int64) : Any\n def self.write_value(val : UInt8) : Any\n # A TOML integer is an `Int64`: a `UInt64` past it cannot be written.\n def self.write_value(val : UInt64) : Any\n def self.write_value(val : Float64) : Any\n def self.write_value(val : String) : Any\n def self.write_value(val : TOMLDateTime) : Any\n def self.write_value(val : Any) : Any\n def self.write_value(val : Array(T)) : Any forall T\n # TOML keys are strings, so a hash is written when its keys are.\n def self.write_value(val : Hash(String, V)) : Any forall V\n def self.write_value(val : T) : Any forall T : ToTOML\nend\n\n# A TOML date, time or date-time, with its kind. An offset date-time has\n# every field and `offset_minutes` (0 for `Z`); a local date-time every\n# field but the offset; a local date the date fields alone, a local time\n# the time fields alone, the others 0. Two values are equal when their kind\n# and every field are: `07:32:00Z` and `08:32:00+01:00` are one instant\n# written two ways, and this keeps which way (`unix_seconds` says they are\n# the same instant).\npub struct TOMLDateTime\n def !=(other : TOMLDateTime) : Bool\n def ==(other : TOMLDateTime) : Bool\n def day : Int32\n def self.days_in_month(year : Int32, month : Int32) : Int32\n def hash : Int32\n def hour : Int32\n # Fields that do not make a value of the kind are the caller's mistake,\n # refused with what is wrong; `problem` says it without panicking.\n def initialize(kind : Int32, year : Int32, month : Int32, day : Int32, hour : Int32, minute : Int32, second : Int32, nanosecond : Int32, offset_minutes : Int32)\n def inspect : String\n def kind : Int32\n def kind_name : String\n def self.leap_year?(year : Int32) : Bool\n def self.local_date(year : Int32, month : Int32, day : Int32) : TOMLDateTime\n def local_date? : Bool\n def self.local_date_time(year : Int32, month : Int32, day : Int32, hour : Int32, minute : Int32, second : Int32, nanosecond : Int32 = 0) : TOMLDateTime\n def local_date_time? : Bool\n def self.local_time(hour : Int32, minute : Int32, second : Int32, nanosecond : Int32 = 0) : TOMLDateTime\n def local_time? : Bool\n def minute : Int32\n def month : Int32\n def nanosecond : Int32\n def offset? : Bool\n def self.offset_date_time(year : Int32, month : Int32, day : Int32, hour : Int32, minute : Int32, second : Int32, nanosecond : Int32 = 0, offset_minutes : Int32 = 0) : TOMLDateTime\n def offset_minutes : Int32\n # `value` in decimal with zeros in front to `width` digits.\n def self.pad(value : Int32, width : Int32) : String\n # Nil when the fields make a value of `kind`, else what is wrong with\n # them, as the reader and `new` both say it.\n def self.problem(kind : Int32, year : Int32, month : Int32, day : Int32, hour : Int32, minute : Int32, second : Int32, nanosecond : Int32, offset_minutes : Int32) : String | ::Nil\n def second : Int32\n # As TOML writes it (RFC 3339 for an offset date-time): `T` between\n # date and time, `Z` for a zero offset, the fraction with its trailing\n # zeros dropped and left out when it is zero.\n def to_s : String\n # Seconds since 1970-01-01T00:00:00Z of an offset date-time, the\n # fraction left in `nanosecond`. The other three kinds name no instant.\n def unix_seconds : Int64\n def year : Int32\nend\n\n# What is wrong with a document, and where: `line` and `column` count from\n# 1, a column in characters. `message` is \" at line L, column C\".\npub struct TOMLError\n def column : Int32\n def initialize(reason : String, line : Int32, column : Int32)\n def line : Int32\n def reason : String\n def to_s : String\nend\n\n# What `TOML.write_value` and `TOML.to_any` take besides the built-in\n# types: a value whose type implements it, as a `derive\n# toml_serializable` type does.\npub trait ToTOML\n abstract def to_toml_any : Any\nend\n\nimpl Error for TOMLError\n def message : String\nend\n" }, { @@ -634,85 +640,91 @@ { "name": "std/tuple", "path": "src/std/tuple.iyi", - "sourceSha256": "5a67030cf1aec1ddb1dd137df717cdb0d44df9e0edd3714ab92754c6a5a86e1a", + "sourceSha256": "49754c7a5af3d05017c89bbb20e3453d68a78ca64bb54f3381a681026a9f4d77", "surface": "module std/tuple\n\nstruct ::NamedTuple(T)\n # Compares the values by name.\n #\n # Any named tuple: the keys are sorted and compared, then the values in\n # that order as tuples compare (src/iyi/object.iyi), member by member\n # where one type holds the other. `self` took `{a: 1, b: 2}` for `{a: 1,\n # b: y}` (y : Int32?) and not back, so the pair answered true one way and\n # false the other, and `case` over them did not match.\n def ==(other : NamedTuple) : Bool\n # Yields each key symbol and corresponding value.\n def each : Nil\n # Yields each value in order.\n def each_value : Nil\n # Returns true when the named tuple contains zero entries.\n def empty? : Bool\n # Returns true if the named tuple contains the given key symbol.\n #\n # The keys here and below are written with `symbolize`, never as\n # `:{{key.id}}`: a quoted key such as `\"content-type\"` or `\"x y\"` pasted\n # that way is `:content-type`, a symbol minus a call, or `:x y`, which\n # does not parse, so every one of these methods failed to compile for\n # such a key. `std/named_tuple` already wrote them this way.\n def has_key?(key : Symbol) : Bool\n # Hashing follows the `==` below, mixed as `Tuple#hash` mixes the members\n # (src/iyi/object.iyi). `Object#hash` answered the type's id, so\n # `{a: 1}` and `{a: 2}` hashed alike and 20,000 keys took 1.4 s to insert\n # where the same pairs as tuples took 1 ms. The values go in by sorted\n # key, because `==` matches by name: `{x: 1, y: 2} == {y: 2, x: 1}`, so\n # the two have to hash alike although their types list the keys in\n # another order.\n def hash : Int32\n def initialize\n def inspect : String\n # Returns a Tuple containing the key symbols in order. Built with\n # `Tuple.new`, as `Tuple#map` is: for the named tuple with no keys,\n # braces are an empty hash literal.\n def keys\n # Value identity.\n def same?(other : self) : Bool\n # Number of key-value pairs in the named tuple.\n def size : Int32\n # The keys, and the values, in sorted key order: the order a type lists\n # its keys in is not part of the value.\n protected def sorted_keys\n protected def sorted_values\n # String representation: `{name: \"Alice\", age: 30}`. A key that is not a\n # name is quoted, as it is written: `{\"content-type\": \"text/plain\"}`.\n def to_s : String\n # Returns a Tuple containing the values in order.\n def values\nend\n\nstruct ::Tuple(*T)\n # Concatenates two tuples.\n def +(other : Tuple(*U)) forall U\n # Yields each element sequentially.\n def each : Nil\n # Yields each element alongside its index.\n def each_with_index : Nil\n # Returns true when the tuple contains zero elements.\n def empty? : Bool\n # Returns the first element. Raises if empty.\n def first\n # Returns the first element, or nil if empty.\n def first?\n # Returns the last element. Raises if empty.\n def last\n # Returns the last element, or nil if empty.\n def last?\n # Returns a new Tuple with each element mapped through the block.\n #\n # This, `+`, `reverse`, and `NamedTuple#keys` and `#values` build with\n # `Tuple.new(...)`, not braces: with no members `{ }` is an empty hash\n # literal, so on the empty tuple each failed to compile with \"for empty\n # hashes use '{} of KeyType => ValueType'\".\n def map\n # Creates a new Tuple containing the given arguments.\n def self.new(*args : *T)\n # Reverses elements in tuple.\n def reverse\n # Value identity.\n def same?(other : self) : Bool\n # Converts the tuple into an Array typed as the union of all element types.\n # The empty tuple's is `[] of NoReturn`, the union of no types: the\n # `typeof` below had no argument there, and `Tuple.new.to_a` did not\n # compile.\n def to_a\nend\n" }, { "name": "std/udp", "path": "src/std/udp.iyi", - "sourceSha256": "de67e123e5505406321b5f1cf7626e43fedf141ada0f9e89a667f3ab7cc0d858", + "sourceSha256": "b14d7adf6211f768389773c8eb6062368c4ee5f538dd0047a46f46402d89e96c", "surface": "module std/udp\n\npub alias Bytes = Std::Slice::Bytes\n\n# An incoming datagram with payload bytes and remote peer origin.\npub struct Datagram\n def [](i : Int32) : Bytes | String | Int32\n def data : Bytes\n def host : String\n def initialize(data : Bytes, host : String, port : Int32)\n def port : Int32\n # Decodes payload bytes as a String.\n def text : String\n def to_s : String\n def to_tuple : Tuple(Bytes, String, Int32)\nend\n\n# The address types, their parsers and the `sockaddr_in`/`sockaddr_in6`\n# writers are `std/socket`'s; this module had copies, and the copies had\n# drifted: its IPv6 parser read no dotted tail and its printer compressed\n# only `::` and `::1`.\npub alias IPv4Address = Std::Socket::IPv4Address\n\npub alias IPv6Address = Std::Socket::IPv6Address\n\npub class UdpSocket\n alias SockLen = Int64\n def self.__is_eagain(res : Int64) : ::Bool\n def self.__is_eintr(res : Int64) : ::Bool\n # No such refusal: a datagram larger than the buffer fills it, the\n # rest is dropped and the length answered is what fit.\n def self.__is_msgsize(res : Int64) : ::Bool\n # ECONNREFUSED (111): the ICMP port-unreachable a *connected* datagram\n # socket's earlier send provoked, which Linux and BSD report once, to\n # the next send or receive on it (udp(7)), and then forget; an\n # unconnected one is never told. It is something the peer did, not a\n # failure of this socket, so the shared bodies read it as nothing\n # queued and send again. Read as a failure, the next `send` or\n # `receive?` panicked here, where Windows, which has the report turned\n # off, answered the byte count and nil. A `poll_read` the report wakes\n # still answers true [INFERENCE], and the `receive?` after it nil.\n def self.__is_refused(res : Int64) : ::Bool\n def self.__is_unbound(res : Int64) : ::Bool\n def self.__sys3(number : Int64, a : Int64, b : Int64, c : Int64) : Int64\n def self.__sys5(number : Int64, a : Int64, b : Int64, c : Int64, d : Int64, e : Int64) : Int64\n def self.__sys6(number : Int64, a : Int64, b : Int64, c : Int64, d : Int64, e : Int64, f : Int64) : Int64\n def self.__sys_bind(fd : Int32, addr : Pointer(UInt8), len : Int64) : Int32\n def self.__sys_connect(fd : Int32, addr : Pointer(UInt8), len : Int64) : Int32\n def self.__sys_get_buffer_size(fd : Int32, level : Int32, optname : Int32) : Int32\n def self.__sys_getsockname(fd : Int32, addr : Pointer(UInt8), addrlen : Pointer(UInt32)) : Int32\n def self.__sys_poll_read(fd : Int32, timeout_ms : Int32) : ::Bool\n def self.__sys_recvfrom(fd : Int32, buf : Pointer(UInt8), len : UInt64, flags : Int32, addr : Pointer(UInt8), addrlen : Pointer(UInt32)) : Int64\n def self.__sys_send(fd : Int32, buf : Pointer(UInt8), len : UInt64) : Int64\n def self.__sys_sendto(fd : Int32, buf : Pointer(UInt8), len : UInt64, flags : Int32, addr : Pointer(UInt8), addrlen : Int64) : Int64\n def self.__sys_set_buffer_size(fd : Int32, level : Int32, optname : Int32, size : Int32) : Int32\n def self.__sys_socket(domain : Int32 = 2) : Int32\n def self.__unbound_now? : ::Bool\n # Binds a UDP socket to the specified host and port.\n # Automatically detects whether host is an IPv4 or IPv6 address.\n #\n # Without SO_REUSEADDR, on every platform. It waives TIME_WAIT for a\n # listener, and a datagram port never has one; what it does to a\n # datagram socket on Linux is let a second socket bind the same address\n # and port while the first holds it, and unicast then reaches only one\n # of them. The POSIX arms asked for it and the win32 arm did not, so a\n # second bind of a held port failed on Windows, measured, and went\n # through on Linux [INFERENCE: not run here].\n def self.bind(host : String, port : Int32) : UdpSocket\n # Binds a UDP socket to 0.0.0.0 and the specified port.\n def self.bind(port : Int32) : UdpSocket\n # Binds an IPv6 UDP socket to [::] and the specified port.\n def self.bind6(port : Int32) : UdpSocket\n # `bind`, with a refused bind answered rather than raised: the port\n # held, reserved (Windows' excluded ranges answer \"access denied\"), or\n # the address not this machine's - so a program can pick another. An\n # error's `message` is the sentence `bind` raises.\n def self.bind?(host : String, port : Int32) : UdpSocket | SocketError\n # Creates an unbound UDP client socket.\n def self.client(family : Int32 = 2) : UdpSocket\n # Creates an unbound IPv6 UDP client socket.\n def self.client_v6 : UdpSocket\n # Closes the socket file descriptor.\n def close : Nil\n def closed? : ::Bool\n # Connects the UDP socket to a remote host and port (sets default peer).\n def connect(host : String, port : Int32) : Nil\n def family : Int32\n def fd : Int32\n def self.format_address(addr : Pointer(UInt8)) : Tuple(String, Int32)\n def initialize(fd : Int32 = -1, family : Int32 = 2)\n # Reads back the locally bound IP address.\n def local_address : String\n # Reads back the locally bound port assigned by the OS.\n def local_port : Int32\n def self.make_sockaddr(host : String, port : Int32) : Tuple(Pointer(UInt8), SockLen, Int32)\n # Creates an unbound UDP socket.\n def self.open(family : Int32 = 2) : UdpSocket\n # Polls whether the socket is readable within timeout_ms milliseconds.\n # Returns true if a datagram is ready to be read, false on timeout.\n def poll_read(timeout_ms : Int32 = 0) : ::Bool\n # Receives payload bytes from the connected peer, parking until some arrive.\n def receive(max_bytes : Int32 = 65536) : Bytes | Cancelled\n # Payload bytes queued now from the connected peer, or nil.\n def receive?(max_bytes : Int32 = 65536) : Bytes | ::Nil\n # Returns the current receive buffer size (SO_RCVBUF).\n def receive_buffer_size : Int32\n # Sets the receive buffer size (SO_RCVBUF).\n def receive_buffer_size=(size : Int32) : Nil\n # Receives a Datagram struct containing data, host, and port.\n def receive_datagram(max_bytes : Int32 = 65536) : Datagram | Cancelled\n # A Datagram of what is queued now, or nil.\n def receive_datagram?(max_bytes : Int32 = 65536) : Datagram | ::Nil\n # Receives a datagram into a newly allocated Bytes slice up to max_bytes.\n # Answers {payload, peer_host, peer_port}, parking until one arrives.\n def receive_from(max_bytes : Int32 = 65536) : Tuple(Bytes, String, Int32) | Cancelled\n # Receives a datagram into a caller-supplied Bytes buffer.\n # Answers {subslice_of_received_bytes, peer_host, peer_port}.\n def receive_from(buffer : Bytes) : Tuple(Bytes, String, Int32) | Cancelled\n # What is queued now, or nil. Never parks.\n def receive_from?(max_bytes : Int32 = 65536) : Tuple(Bytes, String, Int32) | ::Nil\n # Into a buffer: what is queued now, or nil. Never parks.\n def receive_from?(buffer : Bytes) : Tuple(Bytes, String, Int32) | ::Nil\n # Why a call failed: `std/socket`'s sentence for the platform's\n # number (`SocketError#reason`), and the refusals only a datagram meets\n # named as well. Each refusal was a fixed sentence that dropped the\n # number, so a broadcast, a datagram past 65,507 bytes, a port in use\n # and an IPv6 address from an IPv4 socket all said only \"cannot send to\n # H:P\" or \"cannot bind UDP socket to H:P\" - measured on Windows.\n def self.refusal(res : Int64, sending : ::Bool) : String\n # Sends bytes to the connected peer.\n def send(bytes : Bytes) : Int32\n # Sends string to the connected peer.\n def send(str : String) : Int32\n # Returns the current send buffer size (SO_SNDBUF).\n def send_buffer_size : Int32\n # Sets the send buffer size (SO_SNDBUF).\n def send_buffer_size=(size : Int32) : Nil\n # Sends a raw buffer to a destination host and port.\n def send_to(buffer : Pointer(UInt8), count : Int32, host : String, port : Int32) : Int32\n # Sends bytes to a destination host and port.\n def send_to(bytes : Bytes, host : String, port : Int32) : Int32\n # Sends string contents as a datagram to a destination host and port.\n def send_to(str : String, host : String, port : Int32) : Int32\n def to_unsafe : Int32\n # Creates an unbound UDP socket.\n def self.unbound(family : Int32 = 2) : UdpSocket\nend\n" }, { "name": "std/unicode", "path": "src/std/unicode.iyi", - "sourceSha256": "2d49cc9a3d25a0d4f8045244297fa767c6794ef4f6c94a57b5429018cd76f79c", + "sourceSha256": "9992a2b616b097bbc49af0c92a7c5db71251162c2b028a67e88170dc4658d291", "surface": "module std/unicode\n\npub struct Unicode\n # Options the case mappings take; combine them with `|`.\n enum CaseOptions : Int32\n ASCII = 1\n Turkic = 2\n Fold = 4\n end\n # The normalization forms `normalize` writes.\n enum NormalizationForm : Int32\n NFD = 0\n NFC = 1\n NFKD = 2\n NFKC = 3\n end\n def self.ascii?(char : Char) : Bool\n # The first character in titlecase and the rest in lowercase: `\"džemal\"` is\n # `\"Džemal\"`, `\"STRASSE\"` is `\"Strasse\"`. The first character is still a\n # letter before the rest, so `\"ΑΣ\"` is `\"Ας\"`.\n def self.capitalize(text : String, options : CaseOptions = CaseOptions::None) : String\n # Every character whose simple case folding is this one's, itself among\n # them, in code point order: `k` answers `K`, `k` and the Kelvin sign\n # U+212A, `σ` answers `Σ`, `ς` and `σ`, and `ß` answers `ß` and `ẞ`. A\n # character without case answers itself alone. It is what a\n # case-insensitive comparison of one character has to accept, and folding\n # both sides is not enough to build it: a class holding the Kelvin sign\n # has to take `K` as well, and only the folding read backwards says so.\n #\n # The simple folding here is CaseFolding.txt's C and S rows, which is\n # RE2's. `foldcase` answers the other library's, which has no S rows:\n # there `ẞ` folds to itself.\n def self.case_orbit(char : Char) : Array(Char)\n # The string with every maximal ill-formed subpart replaced by U+FFFD; a\n # well-formed string is answered as it is.\n def self.clean(text : String) : String\n # The bytes as a string, every maximal ill-formed subpart replaced by U+FFFD.\n def self.clean(bytes : Array(UInt8)) : String\n # Category C: Cc, Cf, Cs and Co — controls, format characters,\n # surrogates and private use.\n def self.control?(char : Char) : Bool\n # The string the UTF-16 code units spell. An unpaired surrogate is refused\n # with its index: `unpaired surrogate 0xD800 at index 3`.\n def self.decode_utf16(units : Array(UInt16)) : String\n # The string the UTF-16 code units spell, or nil at an unpaired surrogate.\n def self.decode_utf16?(units : Array(UInt16)) : String | ::Nil\n # The lowercase of one character, the simple mapping: `İ` becomes `i`\n # under Turkic rules and stays `İ` otherwise, because its full mapping is\n # `i` plus a combining dot.\n def self.downcase(char : Char, options : CaseOptions = CaseOptions::None) : Char\n # The full lowercase mapping, one character at a time.\n def self.downcase(char : Char, options : CaseOptions = CaseOptions::None, & : (Char -> Nil)) : Nil\n # The string in lowercase, full mappings, with the final sigma: a capital\n # sigma that ends a word — a cased letter before it, none after, the\n # case-ignorable characters (combining marks, format characters, modifier\n # letters and the apostrophes, periods and colons that can sit inside a\n # word) looked through — becomes `ς`, so `\"ΟΔΥΣΣΕΥΣ\"` is `\"οδυσσευς\"`.\n # With `CaseOptions::Fold` this is `foldcase`.\n def self.downcase(text : String, options : CaseOptions = CaseOptions::None) : String\n # Every orbit `case_orbit` answers that holds more than one character,\n # each once, as code points in order and in no particular order between\n # them. A case-insensitive class folds itself against these, about 1,400\n # of them, rather than asking `case_orbit` of each of the hundred\n # thousand code points a class like `\\p{L}` holds.\n def self.each_case_orbit(& : (Array(Int32) -> Nil)) : Nil\n # Each grapheme cluster, in order.\n def self.each_grapheme(text : String, &block : (String -> _)) : Nil\n # The string's UTF-16 code units: one per character up to U+FFFF, a\n # surrogate pair above.\n def self.encode_utf16(text : String) : Array(UInt16)\n # The case folding of one character where it is one code point (`Σ`, `σ`\n # and `ς` all fold to `σ`); a character whose folding is longer, like `ß`,\n # is answered unchanged — the block form and the string form fold it.\n def self.foldcase(char : Char) : Char\n # The full case folding, one character at a time: `ß` yields `s` twice.\n def self.foldcase(char : Char, & : (Char -> Nil)) : Nil\n # The string folded for comparison: `\"Straße\"` and `\"STRASSE\"` both fold\n # to `\"strasse\"`.\n def self.foldcase(text : String) : String\n # The two-letter general category of a character: `\"Lu\"` for `A`, `\"Po\"`\n # for `!`, `\"Sm\"` for `+`; `\"Cn\"` for a code point no character is\n # assigned to.\n def self.general_category(char : Char) : String\n # How many grapheme clusters the string has: what a reader counts.\n def self.grapheme_size(text : String) : Int32\n # The string's extended grapheme clusters, as UAX #29 divides them: what\n # a reader sees as one character - `e` and a combining acute, a flag's\n # two regional indicators, a family emoji joined by ZWJs, a Devanagari\n # conjunct - is one string. A piece of ill-formed UTF-8 is a cluster of\n # its own, as the U+FFFD that stands for it would be.\n def self.graphemes(text : String) : Array(String)\n def initialize\n # Category L: Lu, Ll, Lt, Lm and Lo.\n def self.letter?(char : Char) : Bool\n # Category Ll.\n def self.lowercase?(char : Char) : Bool\n # Category M: Mn, Mc and Me — the combining marks.\n def self.mark?(char : Char) : Bool\n # The string in the given normalization form, NFC when none is named.\n def self.normalize(text : String, form : NormalizationForm = NormalizationForm::NFC) : String\n # Whether the string is already in the given form.\n def self.normalized?(text : String, form : NormalizationForm = NormalizationForm::NFC) : Bool\n # Category N: Nd, Nl and No — digits in every script, letter numbers, and\n # the fractions and superscripts.\n def self.number?(char : Char) : Bool\n # The code points a property names, as sorted `{from, to}` pairs that\n # neither overlap nor touch: a general category (`\"Lu\"`), a major class\n # by its letter (`\"L\"` is Lu, Ll, Lt, Lm and Lo; `\"C\"` is Cc, Cf, Cs and\n # Co, unassigned code points being in no category), or a script\n # (`\"Greek\"`). Nil for a name that is none of those. A class is built\n # from the ranges once rather than asking per character, which is what a\n # regex's `\\p{..}` needs: ranges it can negate and case-fold.\n def self.property_ranges(name : String) : Array(Int32) | ::Nil\n # The script a character is written in, as `Scripts.txt` names it:\n # `\"Latin\"`, `\"Greek\"`, `\"Han\"`, and `\"Common\"` for the digits and\n # punctuation every script shares; `\"Unknown\"` for a code point the file\n # does not list.\n def self.script(char : Char) : String\n # The titlecase of one character, the simple mapping: the uppercase, except\n # for the digraphs, where `dž` and `DŽ` both become `Dž`.\n def self.titlecase(char : Char, options : CaseOptions = CaseOptions::None) : Char\n # The full titlecase mapping, one character at a time: `ß` yields `S`, `s`.\n def self.titlecase(char : Char, options : CaseOptions = CaseOptions::None, & : (Char -> Nil)) : Nil\n # Category Lt: the digraphs whose first half is capital, `Dž` and `ᾈ`.\n def self.titlecase?(char : Char) : Bool\n # The uppercase of one character: the simple mapping, so `ß` stays `ß`\n # (its full mapping is two letters; the block form gives it).\n def self.upcase(char : Char, options : CaseOptions = CaseOptions::None) : Char\n # The full uppercase mapping, one character at a time: `ß` yields `S` twice,\n # `ʼn` yields `ʼ` and `N`.\n def self.upcase(char : Char, options : CaseOptions = CaseOptions::None, & : (Char -> Nil)) : Nil\n # The string in uppercase, full mappings: `\"straße\"` is `\"STRASSE\"`.\n # An ASCII-only string is the prelude's `upcase`, unless the Turkic rule\n # is on, which is a question about `i` itself.\n def self.upcase(text : String, options : CaseOptions = CaseOptions::None) : String\n # Category Lu.\n def self.uppercase?(char : Char) : Bool\n # Whether the string's bytes are well-formed UTF-8.\n def self.valid?(text : String) : Bool\n # Whether the bytes are well-formed UTF-8.\n def self.valid?(bytes : Array(UInt8)) : Bool\n # Category Z: Zs, Zl and Zp — the spaces and the line and paragraph\n # separators. Tab and newline are controls, not whitespace, here.\n def self.whitespace?(char : Char) : Bool\nend\n\nclass ::String\n def each_grapheme(&block : (String -> _)) : Nil\n def grapheme_size : Int32\n def graphemes : Array(String)\nend\n" }, + { + "name": "std/unit", + "path": "src/std/unit.iyi", + "sourceSha256": "c3aded0f16887a3d7de94a91fce8ff019d294242deb7109737e3d55ee4c0d578", + "surface": "module std/unit\n\n# A value that only one unit's tasks reach, one task at a time: the block\n# `Unit.new` runs makes it, and `g.spawn(in: u) { |value| }` runs a task of\n# `g` in its unit, handed it (the header above; SPEC.md III.4.18).\npub class Unit(T) < ::IyiGroup::Unit(T)\n protected def self.build(value : T) : Unit(T)\n # A `Unit` owning the value *block* makes. The block may capture only\n # `Share` values: it is the value's way in (`CleanupTransformer#check_share_boundary`).\n def self.new(&block : (-> U)) : Unit(U) forall U\nend\n" + }, { "name": "std/uri", "path": "src/std/uri.iyi", - "sourceSha256": "d8415c047bdcf59561a5bd1a38e4a8ac058dc0519d27c38b3f92e39b4493d7ba", + "sourceSha256": "f093947588f22b640e8bc34781462cda8acc92cb7fd377634ce483cfc3507257", "surface": "module std/uri\n\n# An ordered multi-value collection representing URI query parameters\n# or application/x-www-form-urlencoded form data.\npub struct Params\n # Returns the first value for *key*, or raises if not found.\n def [](key : String) : String\n # Sets the value for *key*, replacing all previous occurrences.\n def []=(key : String, value : String) : Nil\n # Returns the first value for *key*, or nil if not found.\n def []?(key : String) : String | ::Nil\n # Appends a key-value pair, preserving duplicate keys.\n def add(key : String, value : String) : Nil\n # Deletes all entries for *key*, returning the removed values or nil.\n def delete(key : String) : Array(String) | ::Nil\n # Iterates through each key-value pair.\n def each(& : (String, String -> Nil)) : Nil\n def empty? : Bool\n # Returns all values associated with *key*.\n def fetch_all(key : String) : Array(String)\n def has_key?(key : String) : Bool\n def initialize(keys : Array(String), values : Array(String))\n def initialize\n # Parses a query string into Params, decoding each name and value the\n # way the WHATWG URL standard reads `application/x-www-form-urlencoded`:\n # `+` is a space, `%XX` is the byte, and a `%` that does not start two\n # hex digits is itself. Pairs are separated by `&` and also by `;`, as\n # the other library separates them, where the standard and Python\n # 3.12's `parse_qsl` split at `&` alone: `a=1;b=2` is two pairs here and\n # one there.\n # Never a panic - the query is whatever a client sent, and `?q=100%`\n # is what browsers send for \"100%\". `URI.decode` stays strict: it is\n # asked of text a program made.\n def self.parse(query : String) : Params\n def size : Int32\n # Encodes parameters to application/x-www-form-urlencoded string.\n def to_s : String\nend\n\npub class URI\n # The userinfo is compared as written, as the path is: `u%3Btype%3Da` and\n # `u;type=a` are two URIs (RFC 3986 section 2.2).\n def ==(other : URI) : Bool\n def absolute? : Bool\n # *host* as DNS looks it up and an HTTP `Host` header carries it: a\n # registered name's escapes decoded, since RFC 3986 section 3.2.2 has\n # them stand for UTF-8, and the name carried by `IDNA.to_ascii`, so\n # `Bücher.example` and `b%C3%BCcher.example` are both\n # `xn--bcher-kva.example`. An IP literal in brackets and an empty host\n # (`file:///x`) are answered as they are. What `IDNA.to_ascii` refuses\n # is its `IdnaError`, and so is a malformed escape. `hostname` is not\n # this: it answers what the reference wrote.\n def self.ascii_host(host : String) : String | IdnaError\n # `userinfo@host:port`, the userinfo and the host written as stored.\n def authority : String | ::Nil\n def self.decode(str : String, plus_to_space : Bool = false) : String\n # Decodes `%XX` escapes (and `+` to a space when *plus_to_space*).\n # `decode?` answers nil and `decode` panics for a `%` not followed by\n # two hex digits; a multi-byte UTF-8 character encoded byte by byte\n # comes back as the character.\n def self.decode?(str : String, plus_to_space : Bool = false) : String | ::Nil\n # `+` a space and `%XX` a byte, any other `%` kept as it is: the form\n # reading `Params.parse` makes.\n protected def self.decode_form_lenient(str : String) : String\n def self.decode_www_form(str : String, plus_to_space : Bool = true) : String\n def self.decode_www_form?(str : String, plus_to_space : Bool = true) : String | ::Nil\n def self.default_port(scheme : String) : Int32 | ::Nil\n def dup : URI\n # Percent-encodes every byte that is neither unreserved nor reserved,\n # `%` among them: `\"%20\"` becomes `\"%2520\"`, and a space `\"%20\"` (or\n # `\"+\"` when *space_to_plus*, which then writes a `+` as `\"%2B\"`). Bytes\n # above ASCII are encoded one by one, so UTF-8 text round-trips through\n # `decode`.\n def self.encode(str : String, space_to_plus : Bool = false) : String\n # Encodes a path, keeping `/` between its segments.\n def self.encode_path(str : String) : String\n # Encodes one path segment: only unreserved bytes pass.\n def self.encode_path_segment(str : String) : String\n # Encodes a user or a password for the authority: unreserved bytes and\n # the sub-delims, which RFC 3986 section 3.2.1 lets userinfo hold, pass;\n # `:`, which separates the two, and `@`, which ends them, do not.\n def self.encode_userinfo(str : String) : String\n # Encodes a form field the application/x-www-form-urlencoded way: a\n # space is `+` unless *space_to_plus* is false, and only unreserved\n # bytes pass.\n def self.encode_www_form(str : String, space_to_plus : Bool = true) : String\n def fragment : String | ::Nil\n def fragment=(value : String | ::Nil) : String | ::Nil\n # Gen-delims: \":\" / \"/\" / \"?\" / \"#\" / \"[\" / \"]\" / \"@\"\n def self.gen_delim?(byte : UInt8) : Bool\n # Hashing follows `==` (below), the eight parts mixed as a tuple's members\n # are. Without it every URI hashed to its type, so `http://a/` and\n # `https://b:8/x?y#z` hashed alike and a table of 16,000 URI keys took\n # 11.3 s to fill where the same texts as strings took 4 ms.\n def hash : Int32\n def host : String | ::Nil\n # Stored as given: an IPv6 address is written with its brackets,\n # `\"[::1]\"`, the way it appears in a URI, and `hostname` takes them off.\n def host=(h : String | ::Nil) : Nil\n # The host without the brackets an IPv6 address carries.\n def hostname : String | ::Nil\n def initialize(scheme : String | ::Nil = nil, host : String | ::Nil = nil, port : Int32 | ::Nil = nil, path : String = \"\", query : String | ::Nil = nil, user : String | ::Nil = nil, password : String | ::Nil = nil, fragment : String | ::Nil = nil)\n def normalize : URI\n def normalize_in_place : URI\n # The host in lower case and its escapes' hex digits upper-case, the\n # two rules RFC 3986 section 6.2.2.1 gives: `downcase` alone wrote\n # `%C3%A9` as `%c3%a9`, the one spelling of the escape the section\n # normalizes away from.\n protected def self.normalized_host(host : String) : String\n def opaque? : Bool\n def self.parse(raw_url : String) : URI\n # Reads *raw_url* as a URI reference. `parse?` answers nil and `parse`\n # panics with the reason for: a space or control character anywhere\n # (`space at byte 11 in \"http://exa mple.com\"`), a `:` before the first\n # `/`, `?` or `#` whose prefix is not a scheme (`invalid scheme \"ht!tp\"\n # in ...`), a `[` in the host with no `]`, a host with a byte a host\n # cannot hold, and a port that is not decimal digits or is above 65535.\n # Percent escapes are not checked here; `decode` does that.\n def self.parse?(raw_url : String) : URI | ::Nil\n def password : String | ::Nil\n def password=(password : String | ::Nil) : Nil\n def path : String\n def path=(value : String) : String\n def port : Int32 | ::Nil\n def port=(value : Int32 | ::Nil) : Int32 | ::Nil\n def query : String | ::Nil\n def query=(value : String | ::Nil) : String | ::Nil\n def query_params : Params\n def query_params=(params : Params) : Nil\n def relative? : Bool\n # Linear in the path: the input buffer of section 5.2.4 is a cursor into\n # *path*, and each step moves it past what it took. Each step sliced the\n # rest of the path into a new string, so removing the dot segments took\n # time in the square of the path's length: 132 KB of `/a/./b/../c` took\n # 12.8 s in a release build, where splitting it at its slashes took 3 ms.\n def self.remove_dot_segments(path : String) : String\n # The dot segments of a relative reference's path taken out as resolving\n # it against any base would take them: a `..` that climbs past the\n # reference's own segments stays, since only the base knows what it\n # climbs out of, and a path that ends in a dot segment names a\n # directory, so `a/..` is `./`.\n protected def self.remove_relative_dot_segments(path : String) : String\n def self.reserved?(byte : UInt8) : Bool\n # Resolves *ref* against this URI as Base URI according to RFC 3986 section 5.2.\n def resolve(ref : URI | String) : URI\n def scheme : String | ::Nil\n def scheme=(value : String | ::Nil) : String | ::Nil\n # Sub-delims: \"!\" / \"$\" / \"&\" / \"'\" / \"(\" / \")\" / \"*\" / \"+\" / \",\" / \";\" / \"=\"\n def self.sub_delim?(byte : UInt8) : Bool\n def to_s : String\n # Unreserved characters: ALPHA / DIGIT / \"-\" / \".\" / \"_\" / \"~\"\n def self.unreserved?(byte : UInt8) : Bool\n def self.unwrap_ipv6(h : String) : String\n # The user and the password are kept as the reference wrote them, escapes\n # and all, and written back so: decoded by `parse` and re-encoded by\n # `authority`, `u%3Btype%3Da@h` came back `u;type=a@h`, which RFC 3986\n # section 2.2 counts a different URI, and `é` came back `%C3%A9`. The\n # getters decode; the setters and the constructor encode what they are\n # given with `encode_userinfo`.\n def user : String | ::Nil\n def user=(user : String | ::Nil) : Nil\n def userinfo : String | ::Nil\n def userinfo=(info : String | ::Nil) : Nil\n # The user and the password as written, taken as they are.\n protected def written_userinfo(user : String | ::Nil, password : String | ::Nil) : Nil\nend\n" }, { "name": "std/user", "path": "src/std/user.iyi", - "sourceSha256": "3b20fba44ba20dee1a7763d0b0cb249ac2066ca86c7c8612adfd9b458a226121", + "sourceSha256": "e70457afc5f210d7f51cc3aa3feeef404ce7de3f20d3c3016c8d9f80d30a44f4", "surface": "module std/user\n\n# A group: its name, its id, and the accounts listed as its members. A\n# user whose *primary* group this is need not be listed, and usually is\n# not: `User#group_ids` is the question that counts both.\npub struct Group\n # The group whose id is *id*, or nil when there is none or *id* is not a\n # group id; compared as a number, as `User.find_by_id` compares.\n def self.find_by_id(id : String) : Group | ::Nil\n # The group called *name*, or nil when there is none.\n def self.find_by_name(name : String) : Group | ::Nil\n # The group id: a decimal number on Linux and darwin.\n def id : String\n def initialize(name : String, id : String, members : Array(String))\n # The names listed in the group, in the order they are listed.\n def members : Array(String)\n # The group's name: `\"wheel\"`.\n def name : String\n # Every group in *text*, written as `/etc/group` is\n # (`name:password:gid:member,member`), in order, without the lines a\n # lookup skips.\n def self.parse(text : String) : Array(Group)\n # One `/etc/group` line, or nil for a line the lookups skip: blank, a\n # comment, fewer than four fields, a name empty or starting `+` or `-`,\n # or a gid that is not a number. The members are the rest of the line\n # split at commas, empty names dropped, as glibc's list parser drops\n # them.\n protected def self.parse_line(raw : String) : Group | ::Nil\nend\n\n# An account: what `/etc/passwd`, Directory Services or Windows hold for\n# one user.\npub struct User\n # The user this program runs as: the real user id's account. Panics\n # only when the system has no account for that id *and* `$USER` does\n # not name one.\n def self.current : User\n # The account whose user id is *id*, or nil when there is none or *id*\n # is not a user id. On Linux and darwin an id is compared as the number\n # it is: `\"0\"` and `\"00\"` are both root.\n def self.find_by_id(id : String) : User | ::Nil\n # The account called *name*, or nil when there is none.\n def self.find_by_name(name : String) : User | ::Nil\n # The GECOS field up to its first comma; `\"\"` where it is empty.\n def full_name : String\n # The id of the user's primary group, in the same form.\n def group_id : String\n # The ids of the groups out of *groups* this user is in, its primary\n # group first, each once: what `group_ids` answers on Linux, for a group\n # file the caller read and handed to `Group.parse`.\n def group_ids(groups : Array(Group)) : Array(String)\n # The ids of the groups this user is in: its primary group first, then\n # every other the system lists it in, each once.\n def group_ids : Array(String)\n # The home directory.\n def home : String\n # The user id: a decimal number on Linux and darwin, a SID on Windows.\n def id : String\n def initialize(name : String, id : String, group_id : String, full_name : String, home : String, shell : String)\n # The login name: `\"root\"`, `\"ada\"`.\n def name : String\n # Every account in *text*, written as `/etc/passwd` is\n # (`name:password:uid:gid:gecos:home:shell`), in the order it has them,\n # and without the lines a lookup skips: what the Linux lookups read, for\n # a file of the caller's - a container image's, a chroot's.\n def self.parse(text : String) : Array(User)\n # One `/etc/passwd` line, or nil for a line the lookups skip: blank, a\n # comment, fewer than seven fields, a name empty or starting `+` or `-`,\n # or a uid or gid that is not a number. The shell is the rest of the\n # line, colons and all, as glibc reads it.\n protected def self.parse_line(raw : String) : User | ::Nil\n # The login shell; `\"\"` where none is set, and on Windows.\n def shell : String\nend\n" }, { "name": "std/uuid", "path": "src/std/uuid.iyi", - "sourceSha256": "90b5221c32953ec5643c33b350bf248a7620ed6b22bf3e3100c73954c4088616", + "sourceSha256": "bedced8aadb031eea253d8f7e4f016e7a4a2db8c02df5652ce1726e31a1ebdf6", "surface": "module std/uuid\n\npub struct UUID\n def ==(other : UUID) : Bool\n def bytes : Array(UInt8)\n # Hashing follows `==`: the sixteen bytes, as an array of them hashes.\n # Without it every UUID hashed to its type, so the all-zero and the all-f\n # UUIDs hashed alike and a table of 16,000 UUID keys took 2.0 s to fill\n # where the same UUIDs as strings took 5 ms.\n def hash : Int32\n # The sixteen bytes are the UUID's own, copied in here and out of\n # `bytes`, as the other library's are a value: held as the caller's\n # array, a buffer reused for the next record changed every UUID made\n # from it (`b[0] = 255_u8` after `UUID.new(b)` turned `00000000-...`\n # into `ff000000-...`), and `u.bytes[1] = 255_u8` changed a UUID that\n # was already a hash key, which its table then could not find.\n def initialize(bytes : Array(UInt8))\n def self.parse(text : String) : UUID\n protected def stored : Array(UInt8)\n def to_s : String\n # Sixteen bytes from the operating system, six bits of them then set to\n # say version 4 and the RFC variant. Drawn from `Random.new`, they were\n # sixteen outputs of a fresh PCG generator: every UUID was a function of\n # the generator's 127 bits of state, and PCG makes no promise that its\n # outputs do not give that state away, where a version 4 UUID promises\n # 122 bits nobody can predict.\n def self.v4 : UUID\n # RFC 9562 version 7: 48 bits of Unix milliseconds, big-endian, then the\n # version, 12 bits of `rand_a`, the variant and 62 random bits.\n #\n # Two UUIDs from one millisecond would sort at random by their random\n # bits, so `rand_a` is a counter here (the RFC's \"fixed bit-length\n # dedicated counter\", section 6.2 method 1): a new millisecond starts it\n # at a random 11 bits, leaving at least 2,048 steps before it runs out,\n # and every UUID in the same millisecond, or one the clock stepped back\n # into, counts one up from the last. Past 4,095 the stamp moves on a\n # millisecond early, as the RFC allows. Each UUID this process makes\n # therefore sorts after the one before it, as bytes and as text; the\n # 62 bits after the counter are random every time, so two processes in\n # one millisecond still do not collide.\n def self.v7 : UUID\n def version : Int32\nend\n" }, { "name": "std/weak_ref", "path": "src/std/weak_ref.iyi", - "sourceSha256": "75f26c1e72e512019f06f38c193929a4de02dd7886025b8eac8e1c42dd443213", + "sourceSha256": "5f79666884bef80acad4d0a13c21f3584bdcab34f236055e4c14aa1d1b443f88", "surface": "module std/weak_ref\n\npub class WeakRef(T)\n def initialize(target : T)\n def inspect : String\n def to_s : String\n # The target, or nil once a collection has found nothing else holding it.\n def value : T | ::Nil\nend\n" }, { "name": "std/websocket", "path": "src/std/websocket.iyi", - "sourceSha256": "780c73af4a833c7b72347667509e1b21457471ae45f703d76430abda7cb786c6", + "sourceSha256": "32c22c408c7b00c5aaa55f7f28dd1aad70c575d787fbda9546f8d4d943ae2efb", "surface": "module std/websocket\n\n# One whole message: its bytes, and whether it came as text (UTF-8,\n# checked) or binary.\npub struct Message\n def binary? : Bool\n def data : String\n def initialize(data : String, binary : Bool)\n def text? : Bool\nend\n\n# The permessage-deflate (RFC 7692) a handshake agreed to: whether each\n# end starts every message it compresses with an empty window\n# (`*_no_context_takeover`), and how far back each end's matches may\n# reach, as a base-2 logarithm (`*_max_window_bits`, 15 where the answer\n# did not say). `server_` is what the server sends, `client_` what the\n# client sends. `level` is this end's own, not agreed: the DEFLATE level\n# its messages are compressed at, std/compress's 0 (stored) to 9, 1 by\n# default. A window outside 8 to 15, or a level outside 0 to 9, is a\n# panic.\npub struct PerMessageDeflate\n def client_max_window_bits : Int32\n def client_no_context_takeover : Bool\n def initialize(server_no_context_takeover : Bool = false, client_no_context_takeover : Bool = false, server_max_window_bits : Int32 = 15, client_max_window_bits : Int32 = 15, level : Int32 = 1)\n def level : Int32\n def server_max_window_bits : Int32\n def server_no_context_takeover : Bool\nend\n\n# One end of a connection whose handshake is done: what `upgrade` hands\n# its block and `connect` answers. `WebSocket.new(socket, leftover,\n# client)` takes over a socket some other code did the handshake on;\n# *leftover* is what arrived after the handshake's head, read first, and\n# *deflate* the permessage-deflate that handshake agreed to.\npub class WebSocket\n # The `Sec-WebSocket-Accept` that answers *key* (§4.2.2): the base64 of\n # the SHA-1 of the key and the GUID.\n def self.accept(key : String) : String\n # The protocol a server's 101 agreed to (nil for none), or why the\n # answer is not one to the handshake *key* offering *protocols* (§4.1).\n protected def self.agreed(up : Upgraded, key : String, protocols : Array(String)) : String | Nil | WebSocketError\n # The permessage-deflate a server's `Sec-WebSocket-Extensions`\n # (*value*) agreed to, nil for none, or why it is not an answer to an\n # offer of `permessage-deflate; client_max_window_bits` - or, without\n # *offered*, to no offer (RFC 7692 §5, §7). This end compresses at\n # *level*.\n protected def self.answered(value : String | ::Nil, offered : Bool, level : Int32) : PerMessageDeflate | Nil | WebSocketError\n # Past the spaces and tabs from *i*.\n protected def self.blanks(p : Pointer(UInt8), n : Int32, i : Int32) : Int32\n # The close handshake from this end: a close with *code* and *reason*,\n # then the peer's, read past whatever messages it sent first, for up\n # to ten seconds. Nil once it is done, or when the connection was\n # already closed.\n def close(code : Int32 = 1000, reason : String = \"\") : Nil | Cancelled | SocketError | WebSocketError\n # The code the connection closed with: the peer's close's (1005 when it\n # had none), the one this end failed it with, or 1006 when it ended\n # without a close. Nil while it is open.\n def close_code : Int32 | ::Nil\n # Whether *code* is one a close frame may carry (§7.4): 1000 to 1003,\n # 1007 to 1014 (1012 to 1014 as IANA's registry added them), and the\n # ranges for libraries and applications. 1004 is reserved, and 1005,\n # 1006 and 1015 are the codes an endpoint reports, never sends.\n def self.close_code?(code : Int32) : Bool\n protected def self.close_payload(code : Int32, reason : String) : String\n # The reason the peer's close carried; \"\" for none.\n def close_reason : String\n # Whether the connection is closed: after the close handshake, after a\n # failure, and after `close`.\n def closed? : Bool\n # The client's half: the handshake to a `ws://` URL, and the connection\n # it opens. *headers* go with the request (an `Origin`, a cookie); the\n # handshake's own are refused. *protocols* are offered in their order.\n # *proxy* is `HTTP.upgrade`'s: an `http://` proxy's URL, through whose\n # CONNECT tunnel the handshake goes, `ws://` and `wss://` alike; the\n # environment's is `HTTPProxy.from_env` of the URL with `http` for `ws`\n # and `https` for `wss`, the schemes whose variables it reads. With\n # *deflate* permessage-deflate is offered, and messages are compressed\n # at *deflate_level*.\n def self.connect(url : String, headers : Hash(String, String) = {} of String => String, protocols : Array(String) = [] of String, max_message : Int32 = MAX_MESSAGE, proxy : String | ::Nil = nil, deflate : Bool = true, deflate_level : Int32 = 1) : WebSocket | Cancelled | SocketError | HTTPError | WebSocketError\n # The permessage-deflate agreed; nil for none.\n def deflate : PerMessageDeflate | ::Nil\n # What a permessage-deflate offer's or answer's *params* say (RFC 7692\n # §7.1), or why they are not what one may say: each of the four at most\n # once, the two `no_context_takeover`s without a value, the two\n # `max_window_bits` with 8 to 15 in decimal - and `client_max_window_bits`\n # without one in an offer, as a client says it can be held to one.\n protected def self.deflate_terms(params : Array(::Tuple(String, String | ::Nil)), answer : Bool) : DeflateTerms | String\n # *value* as a list of extensions (RFC 6455 §9.1): each a token, with\n # parameters after `;`, a parameter's value a token or a quoted string\n # that is one once unescaped. Why it is not one, otherwise.\n protected def self.extensions(value : String) : Array(Extension) | String\n def initialize(socket : IyiSocket | TlsStream, leftover : String, client : Bool, protocol : String | ::Nil = nil, max_message : Int32 = MAX_MESSAGE, deflate : PerMessageDeflate | ::Nil = nil)\n # Whether *key* is what §4.1 has a client send: 16 bytes in base64,\n # which is 22 characters of the alphabet and `==`.\n protected def self.key?(key : String) : Bool\n # *count* bytes at *p* XORed with *key*'s four bytes in turn, from the\n # first (§5.3): eight bytes a time, then one.\n protected def self.mask(p : Pointer(UInt8), count : Int32, key : UInt32) : Nil\n # A ping, which the peer answers with a pong `receive` reads past.\n def ping(data : String = \"\") : Nil | Cancelled | SocketError | WebSocketError\n def protocol : String | ::Nil\n # The next whole message, or nil when the connection is closed: the\n # close handshake is done, or the connection was already closed.\n def receive : Message | Nil | Cancelled | SocketError | WebSocketError\n # Why *req* is not a handshake this server answers 101, as the answer;\n # nil when it is one.\n protected def self.refusal(req : Request) : Response | ::Nil\n # *text* as one text message. It must be UTF-8.\n def send(text : String) : Nil | Cancelled | SocketError | WebSocketError\n # *data* as one binary message.\n def send_binary(data : String) : Nil | Cancelled | SocketError | WebSocketError\n # The server's half of the handshake, answering *req*: a 101 whose\n # block is handed the connection, or the 400 or 426 that says why the\n # request is not a handshake this server takes. *protocols* are the\n # subprotocols the server speaks, in its order of preference. With\n # *deflate* the first permessage-deflate offer this end can keep is\n # accepted, and messages are compressed at *deflate_level*.\n def self.upgrade(req : Request, protocols : Array(String) = [] of String, max_message : Int32 = MAX_MESSAGE, deflate : Bool = true, deflate_level : Int32 = 1, &handler : (WebSocket -> Nil)) : Response\n # The UTF-8 check's state after the *count* bytes at *p*, from *state*:\n # 0 between characters; the continuation bytes still owed and the range\n # the next must fall in (§3 of RFC 3629: no overlong forms, no\n # surrogates, nothing past U+10FFFF) packed into one number otherwise;\n # -1 once a byte is wrong. A text message is whole when it ends at 0.\n protected def self.utf8_scan(p : Pointer(UInt8), count : Int32, state : Int32) : Int32\n # 8 to 15 for those numbers written in decimal, without a leading zero;\n # 0 for anything else.\n protected def self.window_bits(value : String) : Int32\n # The end of the word from *i*: up to a blank or one of `,;=\"`.\n protected def self.word(p : Pointer(UInt8), n : Int32, i : Int32) : Int32\nend\n\n# What the other end did that RFC 6455 does not allow, or a handshake\n# that is not one, as a value: `reason` is the sentence, `code` the close\n# code it stands for (1002 a protocol error, 1007 bytes that are not\n# UTF-8, 1009 a message too large, 1006 a connection that ended without\n# a close). `message` says it is WebSocket's.\npub struct WebSocketError\n def code : Int32\n def initialize(reason : String, code : Int32 = 1002)\n def reason : String\n def to_s : String\nend\n\nimpl Error for WebSocketError\n def message : String\nend\n" }, { "name": "std/x509", "path": "src/std/x509.iyi", - "sourceSha256": "d89cff9466a3354bf762ce60c9b11e255204257436a4028892ce12bcd1a46e72", + "sourceSha256": "aa88b37fe0e2e70bb4bdbf95b3fee4b0e05ce2e70f9f012876522d3247213d77", "surface": "module std/x509\n\n# A class, not a struct, for the stack's sake. Thirty-odd fields make 472\n# bytes, and a struct is copied whole into every array slot, block\n# argument and union it passes through. In a debug build each copy is a\n# stack slot of its own, and a TLS handshake inside a task ran out of\n# its 256 KiB (issue #130). Measured on Linux x86_64, a struct made\n# `Certificate.parse`'s frame 31,832 bytes (each `!` and early return\n# makes a `Certificate | X509Error`), `X509.grow`'s 13,640 and\n# `issuer_error`'s 6,184; a class makes them 5,224, 296 and 368. With\n# `EcdsaP256.check` split as well (std/ecdsa), `X509.verify` of a\n# two-certificate P-256 chain in a task went from 35,608 bytes of stack\n# to 11,768. Nothing in a certificate is written after `parse`, so\n# sharing one is not observable, and two are equal when they are the\n# same DER, as two structs were when every field was.\npub class Certificate\n # The same certificate: the same DER, from which every field is read.\n def ==(other : Certificate) : Bool\n def authority_key_id : Array(UInt8) | ::Nil\n # basicConstraints: whether it was present, cA, and pathLenConstraint.\n def basic_constraints : Bool\n def ca : Bool\n # subjectAltName's dNSName and iPAddress entries.\n def dns_names : Array(String)\n def excluded_dns : Array(String)\n def excluded_ips : Array(Array(UInt8))\n # extKeyUsage's OIDs; nil when the extension is absent.\n def extended_key_usage : Array(String) | ::Nil\n # Every CERTIFICATE block in a PEM text, read; the first one that is not\n # a certificate is the answer.\n def self.from_pem(text : String) : Array(Certificate) | X509Error\n def hash : Int32\n def initialize(raw : Array(UInt8), tbs : Array(UInt8), version : Int32, serial : Array(UInt8), issuer : Array(UInt8), subject : Array(UInt8), subject_name : String, issuer_name : String, not_before : Time, not_after : Time, public_key : RsaPublicKey | EcdsaPublicKey | Ed25519PublicKey, signature_algorithm : String, signature_parameters : DerValue | ::Nil, signature : Array(UInt8), public_key_bits : Array(UInt8), extensions : X509Extensions)\n def ip_addresses : Array(Array(UInt8))\n # Issuer and subject as their DER: two names are the same name when\n # these are the same bytes.\n def issuer : Array(UInt8)\n def issuer_name : String\n # keyUsage's bits, bit 0 digitalSignature to bit 8 decipherOnly; nil\n # when the extension is absent.\n def key_usage : Int32 | ::Nil\n # Whether *host* is a name this certificate is for: a DNS name or an IP\n # address in its subjectAltName (see the module's header).\n def matches_host?(host : String) : Bool\n # Whether the TLS Feature extension (RFC 7633) asks for status_request:\n # a certificate that must be shown with a stapled OCSP response.\n def must_staple? : Bool\n def not_after : Time\n def not_before : Time\n # Whether id-pkix-ocsp-nocheck is here (RFC 6960 section 4.2.2.2.1): an\n # OCSP responder's certificate saying its own status is not asked.\n def ocsp_nocheck : Bool\n def other_constraints : Bool\n # The certificate *der* holds (RFC 5280 section 4.1).\n def self.parse(der : Array(UInt8)) : Certificate | X509Error\n def path_length : Int32 | ::Nil\n # The DER of every CERTIFICATE block in a PEM text, unread: a store\n # can keep what it can read and count the rest.\n def self.pem_blocks(text : String) : Array(Array(UInt8)) | X509Error\n # nameConstraints: DNS names and IP ranges (address and mask), and\n # whether a constraint of any other kind is there.\n def permitted_dns : Array(String)\n def permitted_ips : Array(Array(UInt8))\n def public_key : RsaPublicKey | EcdsaPublicKey | Ed25519PublicKey\n # The subjectPublicKey BIT STRING's bytes, as written: what an OCSP\n # CertID's issuerKeyHash and a responder's byKey are hashes of (RFC\n # 6960 section 4.1.1).\n def public_key_bits : Array(UInt8)\n # The whole certificate as it was read, and its signed part.\n def raw : Array(UInt8)\n # The serial number's two's-complement bytes, as written.\n def serial : Array(UInt8)\n def signature : Array(UInt8)\n # The signature algorithm's OID, and the signature.\n def signature_algorithm : String\n # Why *issuer*'s key did not make this certificate's signature, or nil.\n def signature_error(issuer : Certificate) : String | ::Nil\n # Whether *issuer*'s key made this certificate's signature.\n def signed_by?(issuer : Certificate) : Bool\n def subject : Array(UInt8)\n def subject_key_id : Array(UInt8) | ::Nil\n # The subject and issuer written out for a person, `CN=..., O=...`.\n def subject_name : String\n def tbs : Array(UInt8)\n # The TLS Feature extension's features (RFC 7633), nil when absent:\n # 5, status_request, is \"must staple\".\n def tls_features : Array(Int32) | ::Nil\n # Critical extensions this does not understand, by OID.\n def unhandled_critical : Array(String)\n def valid_at?(time : Time) : Bool\n def version : Int32\nend\n\n# A private key that signs what this module writes - a certificate for\n# itself, a certificate request - and, through std/acme, an ACME account's\n# requests: P-256 or P-384 (std/p256, std/p384), RSA (std/rsa, 2048 bits\n# or more) or Ed25519 (std/curve25519), each signing in constant time.\n# The signature is the one X.509 carries: ECDSA's the DER of\n# Ecdsa-Sig-Value under SHA-256 on P-256 and SHA-384 on P-384, RSA's PKCS\n# #1 v1.5 under SHA-256, Ed25519's its 64 bytes.\npub struct SigningKey\n # The AlgorithmIdentifier of what `sign` makes: ecdsa-with-SHA256 or\n # -SHA384 without parameters (RFC 5758), sha256WithRSAEncryption with\n # its NULL (RFC 4055), id-Ed25519 without parameters (RFC 8410).\n def algorithm_identifier : Array(UInt8)\n # The Ed25519 key whose 32-byte seed (RFC 8032's private key) is *seed*.\n def self.ed25519(seed : Array(UInt8)) : SigningKey | X509Error\n # The key in *text*'s one PRIVATE KEY block (PKCS #8: P-256, P-384, RSA\n # or Ed25519), EC PRIVATE KEY block (SEC 1, P-256 or P-384) or RSA\n # PRIVATE KEY block (PKCS #1).\n def self.from_pem(text : String) : SigningKey | X509Error\n def initialize(p256 : P256PrivateKey | ::Nil, p384 : P384PrivateKey | ::Nil, rsa : RsaPrivateKey | ::Nil, seed : Array(UInt8) | ::Nil)\n # What the key is, for a person: `P-256`, `P-384`, `RSA-2048`, `Ed25519`.\n def kind : String\n def self.new(key : P256PrivateKey) : SigningKey\n def self.new(key : P384PrivateKey) : SigningKey\n def self.new(key : RsaPrivateKey) : SigningKey\n def p256 : P256PrivateKey | ::Nil\n # A new P-256 key, from the system's entropy (std/random's `Secure`).\n # The 32 bytes it is made from are drawn again in the 2^-32 chance that\n # they are not below the group's order.\n def self.p256 : SigningKey\n def p384 : P384PrivateKey | ::Nil\n # A new P-384 key, as `p256` makes one.\n def self.p384 : SigningKey\n def public_key : RsaPublicKey | EcdsaPublicKey | Ed25519PublicKey\n # The key's SubjectPublicKeyInfo (RFC 5280 section 4.1.2.7): RFC 5480's\n # for P-256 and P-384, RFC 8017's rsaEncryption with its NULL, RFC 8410's\n # for Ed25519.\n def public_key_info : Array(UInt8)\n def rsa : RsaPrivateKey | ::Nil\n # *data* signed as `algorithm_identifier` says.\n def sign(data : Array(UInt8)) : Array(UInt8)\nend\n\npub struct X509\n def initialize\n # The DER of every block labelled *label* in a PEM text - `\"CERTIFICATE\"`\n # or `\"PRIVATE KEY\"` - each checked to be base64 before it is decoded.\n def self.pem(text : String, label : String) : Array(Array(UInt8)) | X509Error\n # A certificate request (PKCS #10, RFC 2986) *key* signs, as its DER: the\n # subject CN= when it fits the 64 characters RFC 5280\n # gives a common name and empty otherwise, the key, and an\n # extensionRequest attribute (RFC 2985 section 5.4.2) asking for\n # subjectAltName with *names* as dNSNames - which is what an ACME server\n # issues for (RFC 8555 section 7.4).\n def self.request(key : SigningKey, names : Array(String)) : Array(UInt8) | X509Error\n # A certificate *key* signs for itself (RFC 5280 section 4.1), as its\n # DER: version 3, *serial* (sixteen random bytes when nil), the subject\n # and issuer both CN=, valid from *not_before* to\n # *not_after*, and the extensions - subjectAltName with *names* as\n # dNSNames, then *extensions* in order. No basicConstraints and no\n # keyUsage: what this writes is a certificate shown for a name, as RFC\n # 8737's tls-alpn-01 shows one, not a CA's.\n #\n # A first name longer than `MAX_COMMON_NAME` is refused, not cut or left\n # out of the subject: a common name holds 64 characters at most, and a\n # subject without one would be empty, which a self-signed certificate's\n # issuer - the same name - must not be (section 4.1.2.4). `request`,\n # whose subject is no issuer, leaves the CN out instead. *serial* is the\n # big-endian magnitude of a positive number: refused when it is zero or\n # when its INTEGER, a sign byte put before a top bit that is set, runs\n # past `MAX_SERIAL_OCTETS` (section 4.1.2.2). The sixteen bytes drawn\n # when it is nil write 17 octets at most and are drawn again in the\n # 2^-128 chance they are all zero.\n def self.self_signed(key : SigningKey, names : Array(String), not_before : Time, not_after : Time, serial : Array(UInt8) | ::Nil = nil, extensions : Array(X509Extension) = [] of X509Extension) : Array(UInt8) | X509Error\n # Why *key*, *signer*'s, did not make *signature* over *signed* under\n # *algorithm* (an OID) and its *parameters*, or nil; *what* names what\n # was signed, for the sentence. A certificate's signature is checked\n # here, and an OCSP response's (std/ocsp).\n def self.signature_error(key : RsaPublicKey | EcdsaPublicKey | Ed25519PublicKey, algorithm : String, parameters : DerValue | ::Nil, signed : Array(UInt8), signature : Array(UInt8), what : String, signer : String) : String | ::Nil\n # The chain from *leaf* to one of *roots*, through *intermediates*, for\n # *host* (nil: any) at *now* - leaf first, root last - or why there is\n # none. *purpose* is what the leaf is for: a server's certificate\n # (serverAuth, the default) or a client's (clientAuth), each certificate\n # that carries extKeyUsage allowing it. See the module's header for what\n # else is checked.\n def self.verify(leaf : Certificate, intermediates : Array(Certificate), roots : Array(Certificate), host : String | ::Nil = nil, now : Time = Time.utc, purpose : X509Purpose = X509Purpose::Server) : Array(Certificate) | X509Error\n # Why *certificate*'s RSA key is under `MIN_RSA_BITS`, or nil.\n def self.weak_key_error(certificate : Certificate) : String | ::Nil\nend\n\n# What reading a certificate, or checking a chain, answers when it fails.\npub struct X509Error\n def initialize(message : String)\n def to_s : String\nend\n\n# An extension a certificate this module writes carries beside its\n# subjectAltName: its OID, whether it is critical, and *value*, the DER\n# that extnValue's OCTET STRING holds.\npub struct X509Extension\n def critical : Bool\n def initialize(oid : String, critical : Bool, value : Array(UInt8))\n def oid : String\n def value : Array(UInt8)\nend\n\n# What a chain is checked for: a TLS server's certificate or a client's\n# (RFC 5280 section 4.2.1.12's id-kp-serverAuth and id-kp-clientAuth).\npub enum X509Purpose : Int32\n Server = 0\n Client = 1\n def holder : String\n def oid : String\n # The usage's name, as a sentence says it.\n def usage : String\nend\n\nimpl Error for X509Error\n def message : String\nend\n" }, { "name": "std/xml", "path": "src/std/xml.iyi", - "sourceSha256": "6fdf39af96bdfa592c1d23a305fe7f2c55c614ec7794ab3faced35e8306aa95c", + "sourceSha256": "bce598b344c6c8c14d7afd35de25269668ed0f248004a2cd5cc3d823e7cebc65", "surface": "module std/xml\n\n# *text* as character data: `&`, `<`, `>` and `\\r` become references,\n# every other byte is copied as it is. With *ascii*, a character past U+007F\n# becomes a decimal reference as well, for a document declared US-ASCII.\npub def escape(text : String, ascii : Bool = false) : String\n\n# *text* for inside a double-quoted attribute value: `\"` and a literal tab\n# or newline become references as well, so the value reads back unchanged.\n# *ascii* is as for `escape`.\npub def escape_attribute(text : String, ascii : Bool = false) : String\n\npub def parse(xml : String, max_depth : Int32 = 256, max_entity_expansions : Int32 = 1000) : Document\n\npub def parse?(xml : String, max_depth : Int32 = 256, max_entity_expansions : Int32 = 1000) : Document | ::Nil\n\npub def unescape(text : String) : String\n\n# The five predefined entities and character references decoded, the same\n# way and with the same refusals as inside a document; nil where `unescape`\n# panics.\npub def unescape?(text : String) : String | ::Nil\n\n# Whether *cp* is a character XML 1.0 allows in a document (production [2]).\npub def xml_char?(cp : Int32) : Bool\n\npub struct Attribute\n def initialize(name : String, value : String, namespace_uri : String = \"\")\n def local_name : String\n def name : String\n # The namespace the prefix was bound to; \"\" for an unprefixed attribute,\n # which has no namespace, not even the default one.\n def namespace_uri : String\n def prefix : String\n # With *ascii*, as `Namespace#to_s`.\n def to_s(ascii : Bool = false) : String\n def value : String\nend\n\npub class Document < Node\n def encoding : String\n def encoding=(value : String) : String\n # The attributes the internal DTD subset declares of type ID, by element\n # name: what XPath's `id()` finds an element by, beside `xml:id`. The\n # rest of the DTD is not kept.\n def id_attributes : Hash(String, Array(String))\n # The attributes the internal DTD subset declares of type ID, by element\n # name: what XPath's `id()` finds an element by, beside `xml:id`. The\n # rest of the DTD is not kept.\n def id_attributes=(value : Hash(String, Array(String))) : Hash(String, Array(String))\n def initialize(version : String = \"1.0\", encoding : String = \"UTF-8\", standalone : Bool | ::Nil = nil)\n def root : Node\n def root? : Node | ::Nil\n def standalone : Bool | ::Nil\n def standalone=(value : Bool | ::Nil) : Bool | ::Nil\n # The declaration and the document's own children are checked as the\n # reader checks them: the declaration first, the shape after each node's\n # own refusals. `version` and `encoding` were written between quotes as\n # they were, so an encoding holding a `\"` closed the declaration and wrote\n # markup of its own (`Document.new(encoding: \"x\\\" standalone=\\\"maybe\")`),\n # and a version that is not `1.` and digits or an encoding this module\n # does not read (`ISO-8859-1`) made a document its parser refuses. So did\n # a second root element, none, text that is not white space, or a CDATA\n # section beside the root.\n def to_xml : String\n def version : String\n def version=(value : String) : String\nend\n\npub struct Namespace\n def default? : Bool\n def initialize(prefix : String, uri : String)\n def prefix : String\n # With *ascii*, a character past U+007F in the uri is written as a\n # character reference, for a document declared US-ASCII.\n def to_s(ascii : Bool = false) : String\n def uri : String\nend\n\npub class Node\n # The same node, by identity. It compared `id`s, and a `Document` counts\n # its own: the first `Document.new` and the first `Node.new_element`\n # were both 1, so the two were `==`, and `add_child`'s question \"is this\n # node one of my ancestors\" would answer yes for a stranger.\n def ==(other : Node) : Bool\n def [](name : String) : String\n def []?(name : String) : String | ::Nil\n # Moves *child* here, as the last child: a node is in one place in a\n # tree. It stayed among its old parent's children as well, so a node\n # moved from `` to `` was written under both and an attribute set\n # on it showed in both places; and a node added under itself or under one\n # of its own descendants made a loop that `to_xml` followed until the\n # stack ran out. That is refused, and so is a document as a child.\n def add_child(child : Node) : Node\n # The parser's own append: the child was just made, under this node, and\n # is in no list yet.\n protected def append_parsed(child : Node) : Node\n # The value of the attribute with exactly this name (`\"x:attr\"` is not\n # `\"attr\"`), or nil.\n def attribute(name : String) : String | ::Nil\n def attributes : Array(Attribute)\n def attributes=(value : Array(Attribute)) : Array(Attribute)\n # What `to_xml` refuses to write (see `serialize_node`): a name that is\n # not an XML name, and a character XML cannot hold raw or as a reference.\n protected def self.check_writable_name(name : String, what : String) : Nil\n protected def self.check_writable_text(text : String) : Nil\n def children : Array(Node)\n def children=(value : Array(Node)) : Array(Node)\n protected def collect_find_all(name : String, res : Array(Node)) : Nil\n protected def collect_text_into(parts : Array(String)) : Nil\n # A text, CDATA, comment or PI node's own text; for an element or a\n # document, the text and CDATA of every descendant, joined.\n def content : String\n def element_children : Array(Node)\n # The first descendant element whose name or local name is *name*.\n def find(name : String) : Node | ::Nil\n # Every descendant element whose name or local name is *name*, in document\n # order.\n def find_all(name : String) : Array(Node)\n def first_child : Node | ::Nil\n def first_element_child : Node | ::Nil\n # The first character past U+007F in *text*, decoded, or -1.\n protected def self.first_non_ascii(text : String) : Int32\n def has_attribute?(name : String) : Bool\n def id : Int32\n def initialize(node_type : NodeType, name : String = \"\", content : String = \"\")\n def inner_xml : String\n def last_child : Node | ::Nil\n def local_name : String\n def local_name=(value : String) : String\n def name : String\n def name=(value : String) : String\n # The namespace this element's name is in: the one its prefix is bound to,\n # or the default namespace in scope, or nil.\n def namespace : Namespace | ::Nil\n def namespace_uri : String\n def namespaces : Array(Namespace)\n def namespaces=(value : Array(Namespace)) : Array(Namespace)\n def self.new_cdata(content : String) : Node\n def self.new_comment(content : String) : Node\n def self.new_element(name : String) : Node\n def self.new_pi(target : String, content : String) : Node\n def self.new_text(content : String) : Node\n def node_type : NodeType\n def node_type=(value : NodeType) : NodeType\n def parent : Node | ::Nil\n def parent=(value : Node | ::Nil) : Node | ::Nil\n # Where this element is, as `/root/item[2]/name[1]`: each step counts\n # same-named element siblings from 1.\n def path : String\n def prefix : String\n def prefix=(value : String) : String\n # `xmlns=\"\"` undeclares the default namespace, so a declaration with an\n # empty uri answers nil rather than being passed over.\n def resolve_default_namespace : Namespace | ::Nil\n def resolve_namespace_prefix(prefix : String) : Namespace | ::Nil\n # The root element of the tree this node is in.\n def root : Node\n # With *ascii* (a document declared US-ASCII), a character past U+007F in\n # text or an attribute value is written as a character reference: written\n # as its UTF-8 bytes, it made a document no other parser reads. A name, a\n # comment and a processing instruction have no references, so one holding\n # such a character is refused; it was written as UTF-8, and this module's\n # own parser refused the result (\"byte 0xC3 is not US-ASCII, the declared\n # encoding\").\n #\n # A name that is not an XML name, and a character XML cannot hold at all\n # (a C0 control but tab, newline and carriage return; U+FFFE, U+FFFF; a\n # byte that is not UTF-8), are refused here: both were written as they\n # were, so `set_attribute(\"k=\\\"1\\\" j\", \"2\")` wrote two attributes and a\n # \"\\u0001\" in text a document no parser reads. A parsed tree holds\n # neither.\n protected def serialize_node(buf : Array(String), ascii : Bool = false) : Nil\n # Sets or replaces the attribute; a prefixed name takes the namespace its\n # prefix is bound to here, \"\" where none is declared.\n def set_attribute(name : String, value : String) : Attribute\n def text : String\n # For a text, CDATA, comment or PI node, its own text; for an element, its\n # children are replaced by one text node with *value*.\n def text=(value : String) : Nil\n def to_s : String\n def to_xml : String\n # Nothing but XML's white space: space, tab, newline, carriage return.\n protected def self.xml_blank?(text : String) : Bool\n # XPath 1.0 (see `XPath`) with this node as the context node: the value\n # the expression is, of whichever type. *namespaces* binds the prefixes\n # the expression uses (a prefix not in it is looked up where it is\n # declared at this node); *variables* gives `$name` its value. Each call\n # compiles *expression*; `XPath.parse` once and `evaluate` many times\n # for a loop.\n def xpath(expression : String, namespaces : Hash(String, String) | ::Nil = nil, variables : Hash(String, XPathValue) | ::Nil = nil) : XPathValue\n def xpath_bool(expression : String, namespaces : Hash(String, String) | ::Nil = nil, variables : Hash(String, XPathValue) | ::Nil = nil) : Bool\n def xpath_float(expression : String, namespaces : Hash(String, String) | ::Nil = nil, variables : Hash(String, XPathValue) | ::Nil = nil) : Float64\n # The first node of that node-set in document order, or nil.\n def xpath_node(expression : String, namespaces : Hash(String, String) | ::Nil = nil, variables : Hash(String, XPathValue) | ::Nil = nil) : XPathNode | ::Nil\n # The node-set *expression* is, in document order; panics when it is a\n # number, a string or a boolean.\n def xpath_nodes(expression : String, namespaces : Hash(String, String) | ::Nil = nil, variables : Hash(String, XPathValue) | ::Nil = nil) : Array(XPathNode)\n # The value converted as `string()`, `number()` and `boolean()` convert.\n def xpath_string(expression : String, namespaces : Hash(String, String) | ::Nil = nil, variables : Hash(String, XPathValue) | ::Nil = nil) : String\nend\n\npub enum NodeType : Int32\n Element = 0\n Text = 1\n CData = 2\n Comment = 3\n ProcessingInstruction = 4\n Document = 5\n def cdata? : Bool\n def pi? : Bool\nend\n\n# The parser keeps the first thing that went wrong in `error` and, once\n# something has, runs its cursor to the end so every loop above it unwinds:\n# `parse?` then answers nil and `parse` panics with that sentence.\npub class Parser\n # An entity's replacement text in an attribute value: a tab, newline or\n # carriage return in it is a space, as one written in the value is. One\n # that a character reference in the replacement text names is not.\n protected def self.attribute_spaces(text : String) : String\n # A value whose declared type is not CDATA, normalised further (XML 1.0,\n # 3.3.3): the spaces at either end dropped and each run of them made one.\n # Only U+0020 counts; a tab a character reference wrote stays. Values\n # were kept as written: `x=\" p q \"` of an NMTOKENS `x` read as is.\n protected def self.collapse_spaces(value : String) : String\n # The sentence the last parse failed with, or \"\" when it did not.\n def error : String\n # Where the first byte at or after *from* that does not begin an XML\n # character is, with the sentence it is refused with; -1 and \"\" when there\n # is none. Only a C0 control is caught where the text is read, so without\n # this a byte that is not UTF-8, an encoded surrogate, U+FFFE and U+FFFF\n # were read and written back, while their character references were\n # refused. With *ascii*, any byte past 0x7F is one.\n protected def self.first_bad_character(input : String, from : Int32, ascii : Bool) : Tuple(Int32, String)\n protected def self.hex4(value : Int32) : String\n def initialize(input : String, max_depth : Int32 = 256, max_expansions : Int32 = 1000)\n # Production [4a]: digits, `-`, `.`, U+00B7, the combining marks\n # U+0300-036F and U+203F-2040 continue a name without starting one.\n protected def self.name_char?(cp : Int32) : Bool\n # Whether all of *text* is one name.\n protected def self.name_shaped?(text : String) : Bool\n # XML 1.0 (fifth edition), production [4]: the characters that start a\n # name, by code point. Any byte past 0x7F was taken as starting and\n # continuing one, so `` read as an element named \"a×b\", a no-break\n # space after a name became part of it, and `<·a/>` was an element.\n protected def self.name_start?(cp : Int32) : Bool\n # `\\r\\n` and a lone `\\r` both mean `\\n` (XML 1.0, 2.11).\n protected def self.normalize_line_ends(input : String) : String\n def parse : Document\n def parse? : Document | ::Nil\n # Production [13]: space, newline, ASCII letters and digits, and\n # -'()+,./:=?;!*#@$_%\n protected def self.public_id_byte?(b : UInt8) : Bool\n # One or more digits, a dot, then digits: the version numbers the other\n # library reads (`1.0`, `1.1`, `1.`).\n protected def self.version_number?(value : String) : Bool\nend\n\n# An XPath 1.0 expression, compiled once and evaluated against any node.\n#\n# All of XPath 1.0 (W3C, 1999): the thirteen axes and the abbreviations\n# (`//`, `.`, `..`, `@`), name tests with prefixes, the node-type tests,\n# predicates by position and by value, filter expressions, `|`, the\n# operators with their conversions (section 3.4's comparisons of node-sets\n# included), variables, and the 27 functions of the core library.\n#\n# A prefix in a name test is looked up in *namespaces*, and when it is not\n# there, in the declarations in scope at the node the expression is\n# evaluated against; `xml` is always bound. An unprefixed name is in no\n# namespace even where a default namespace is declared, as section 2.3\n# says: `` is `a:feed` with `{\"a\" => \"urn:a\"}`, never\n# `feed`.\n#\n# Where libxml2 - the engine under the other library's XPath - departs\n# from the recommendation, this follows the recommendation:\n# * a number becomes the fewest digits that read back as it, written\n# out in full: `string(1 div 3)` is `0.3333333333333333` and\n# `string(-0.0000001)` is `-0.0000001`, where libxml2 writes fifteen\n# digits and `-1e-07`;\n# * a number in an expression is digits and one point: `1e0` is refused;\n# * an attribute's following axis holds its element's descendants,\n# which come after the attribute in document order;\n# * `xmlns=\"\"` leaves no namespace node for the default namespace;\n# * an empty CDATA section is no text node (section 5.7);\n# * `id()` splits its argument on white space wherever it is: libxml2\n# finds nothing for `id(' a')`;\n# * `.//.` (or `/.//.`) as the whole expression holds the context node\n# itself; libxml2 2.9, which darwin carries, leaves it out.\n#\n# `parse` panics on a malformed expression with what was expected and the\n# column; `parse?` answers nil. Evaluation panics on what only a value can\n# show: a function given a number where it takes a node-set, a variable\n# not given, a prefix bound nowhere.\npub class XPath\n # The minimum and maximum argument count of each core function; nil for\n # a name that is not one.\n protected def self.arity(name : String) : Tuple(Int32, Int32) | ::Nil\n def bool(node : Node | XPathNode, namespaces : Hash(String, String) | ::Nil = nil, variables : Hash(String, XPathValue) | ::Nil = nil) : Bool\n # The value at *node*, which is the context node, at position 1 of 1.\n def evaluate(node : Node, namespaces : Hash(String, String) | ::Nil = nil, variables : Hash(String, XPathValue) | ::Nil = nil) : XPathValue\n def evaluate(node : XPathNode, namespaces : Hash(String, String) | ::Nil = nil, variables : Hash(String, XPathValue) | ::Nil = nil) : XPathValue\n def initialize(source : String, expression : XPathExpression, prefixes : Array(String))\n # The node-set the expression is; panics when it is another type.\n def nodes(node : Node | XPathNode, namespaces : Hash(String, String) | ::Nil = nil, variables : Hash(String, XPathValue) | ::Nil = nil) : Array(XPathNode)\n def number(node : Node | XPathNode, namespaces : Hash(String, String) | ::Nil = nil, variables : Hash(String, XPathValue) | ::Nil = nil) : Float64\n # A number as a string (section 4.2): NaN, Infinity, an integer without a\n # point, and otherwise the fewest digits that read back as the number,\n # never with an exponent; both zeros are `0`.\n def self.number_string(value : Float64) : String\n def self.parse(source : String) : XPath\n def self.parse?(source : String) : XPath | ::Nil\n # `round()` (section 4.4): the nearest integer, a tie to the one toward\n # positive infinity, with the sign of a zero kept: `round(-0.5)` is -0.\n def self.round(value : Float64) : Float64\n def source : String\n # XPath's white space: space, tab, carriage return, line feed.\n protected def self.space?(b : UInt8) : Bool\n def string(node : Node | XPathNode, namespaces : Hash(String, String) | ::Nil = nil, variables : Hash(String, XPathValue) | ::Nil = nil) : String\n # A string as a number (section 4.4): white space, an optional minus,\n # digits with at most one point, white space; anything else is NaN.\n def self.string_number(text : String) : Float64\n # `boolean()` of a value.\n def self.to_bool(value : XPathValue) : Bool\n # `number()` of a value.\n def self.to_number(value : XPathValue) : Float64\n # `string()` of a value.\n def self.to_s(value : XPathValue) : String\n protected def self.type_name(value : XPathValue) : String\nend\n\n# The seven kinds of node in XPath 1.0's data model (section 5): the\n# document is the root node, and a CDATA section is a text node.\npub enum XPathKind : Int32\n Root = 0\n Element = 1\n Attribute = 2\n Namespace = 3\n ProcessingInstruction = 4\n Comment = 5\n Text = 6\nend\n\n# A node as an XPath expression sees it. A tree node is itself. An\n# attribute or a namespace node is not a `Node` here - an element keeps its\n# attributes and its declarations as values - so it is the element it\n# belongs to and which of them it is. Two are `==` when they are the same\n# node, which is what a node-set's union and `xpath_node` compare.\npub struct XPathNode\n def ==(other : XPathNode) : Bool\n # The attribute an attribute node is, or nil.\n def attribute : Attribute | ::Nil\n # An attribute node's place in its element's `attributes`, a namespace\n # node's in the element's namespaces in scope ordered by prefix, a text\n # node's among its parent's children where it is known; else -1.\n def index : Int32\n def initialize(node : Node, kind : XPathKind, index : Int32 = -1, namespace_prefix : String = \"\", namespace_target : String = \"\")\n def kind : XPathKind\n # `local-name()`: the name without its prefix.\n def local_name : String\n # `name()`: an element's or attribute's name as written, a processing\n # instruction's target, a namespace node's prefix; \"\" for the rest.\n def name : String\n # A namespace node's prefix (\"\" for the default namespace) and uri.\n def namespace_prefix : String\n def namespace_target : String\n # `namespace-uri()`: an element's or attribute's namespace, else \"\".\n def namespace_uri : String\n # The node itself, or the element an attribute or namespace node is on.\n def node : Node\n # *node* as the tree node it is: a document is the root node, a CDATA\n # section a text node - with the text and CDATA after it, up to the next\n # node of another kind, one text node (section 5.7). *place* is a text\n # node's index among its parent's children, when the caller knows it.\n def self.of(node : Node, place : Int32 = -1) : XPathNode\n # The string-value (XPath 1.0, section 5): every descendant text node's\n # text joined for the root and an element, an attribute's value, a\n # namespace node's uri, a text node's run of text and CDATA, and the\n # text of the rest.\n def text : String\n # A tree node as XML; an attribute as `name=\"value\"`; a namespace node as\n # its declaration.\n def to_s : String\nend\n\n# What an XPath expression evaluates to: a node-set in document order, a\n# number, a string or a boolean.\npub alias XPathValue = Array(XPathNode) | Float64 | String | Bool\n" }, { "name": "std/xz", "path": "src/std/xz.iyi", - "sourceSha256": "8e5c6d320de2b7f869b98d1b99096a1eac92b7efdc815dd0f5576f4a73b99df9", + "sourceSha256": "7577e11e8b33530db81d51c8702f47af2687ee6506acb1e4e984b9601ba10474", "surface": "module std/xz\n\npub struct Xz\n # The bytes *data* decompresses to - every stream of a .xz file, or the\n # one of a .lzma file - or the `XzError` `XzReader` would answer.\n def self.decompress(data : String, format : XzFormat = XzFormat::Xz, dict_limit : Int64 = XzReader::DICT_LIMIT) : String | XzError\n def initialize\nend\n\n# What `XzReader#read` and `Xz.decompress` answer for a stream they cannot\n# read: one that is corrupt, whose check or index or sizes do not match,\n# that ends early, or whose dictionary is past the caller's limit.\npub struct XzError\n def initialize(message : String)\n def to_s : String\nend\n\n# The two files LZMA travels in: the .xz container, and the .lzma file\n# of LZMA Utils and the LZMA SDK.\npub enum XzFormat : Int32\n Xz = 0\n Lzma = 1\nend\n\n# A .xz or .lzma stream read a piece at a time: the compressed bytes come\n# from the block, the decompressed ones go out through `read`, and memory\n# stays at the stream's dictionary and what is waiting to be read.\n#\n# ```\n# xz = XzReader.new { file.read_bytes(65536) }\n# while (piece = xz.read(65536)!) != \"\"\n# ...\n# end\n# ```\npub class XzReader\n # For `Xz.decompress`.\n protected def core : XzCore\n # Whether the stream has ended, everything about it been checked, and\n # every byte been read: the next `read` answers \"\".\n def finished? : Bool\n # *source* answers the next bytes of the compressed stream, nil (or \"\")\n # when there are no more. *dict_limit* is the largest dictionary a\n # stream may name, 4 KiB to 1.5 GiB; a stream that names more is an\n # `XzError` before anything is allocated for it. A .xz file is read to\n # the end of its source - streams and the padding between them - so the\n # source is asked until it says there is no more.\n def initialize(format : XzFormat = XzFormat::Xz, dict_limit : Int64 = DICT_LIMIT, &source : (-> String | ::Nil))\n # Up to *max* decompressed bytes; \"\" once the stream has ended and\n # everything about it been checked. A read answers as soon as it has\n # bytes and going on would call the source, so it can answer fewer than\n # *max* before the end; and never more than the dictionary at once.\n #\n # A corrupt stream, a check, index or size that does not match, input\n # that ends early, or a dictionary past the limit is an `XzError`, after\n # which every read answers the same error. A block's check is compared\n # after its last byte has been handed out, so the bytes before a bad\n # check have been read by then, as they are from a gzip stream. A *max*\n # below 1 is a panic.\n def read(max : Int32 = 65536) : String | XzError\nend\n\nimpl Error for XzError\n def message : String\nend\n" }, { "name": "std/yaml", "path": "src/std/yaml.iyi", - "sourceSha256": "15816e5314695efc6112ff417c5bdc78edb5895f2cb786e76c57643fea98e6fb", + "sourceSha256": "1549d954c95072a4659fd10c5e3822b0c066d23b590291378dde730bc7ee087e", "surface": "module std/yaml\n\n# `derive yaml_serializable`, in a struct or class whose fields are written\n# with their types: the type reads and writes itself as a YAML mapping, a\n# key per field, named as the field is, in the order the fields are\n# written.\n#\n# pub struct Coord\n# getter x : Float64\n# getter name : String\n# derive yaml_serializable\n# end\n#\n# Coord.from_yaml(\"x: 1.5\\nname: a\\n\").x # => 1.5\n# coord.to_yaml # => \"x: 1.5\\nname: a\\n\"\n#\n# It is std/json's `derive serializable` over YAML, field for field: a\n# value is read and written as `YAML.read_value` and `YAML.to_any` read and\n# write its type (the built-in scalars, an `Array` or `Hash` of them, an\n# `Any`, another serializable type, each of them or nil), and `to_yaml`\n# writes through `YAML.dump`, so a string that would read as something\n# else is quoted. A key the type has no field for is passed over; a field\n# with no key keeps its default when it is written with one, as does a\n# field with a default whose key holds null where its type does not admit\n# nil; a field with neither is refused by name unless its type admits nil,\n# which it then holds. Anchors, aliases and `<<` merges are the parser's,\n# so a mapping merged into another fills its fields.\n#\n# The macro is named for its format because a derive names a macro bare:\n# with std/json's `serializable` imported as well, a second `serializable`\n# was refused as \"'serializable' is ambiguous here\", and `derive` takes no\n# qualified name. A type that is both writes `derive serializable,\n# yaml_serializable`; what the two generate shares no method (`to_json`,\n# `from_json` and `initialize(pull)` against `to_yaml`, `from_yaml`,\n# `to_yaml_any` and `initialize(yaml)`).\npub macro yaml_serializable(declaration)\n\n# The value a document holds.\npub struct Any\n alias Type = Nil | Bool | Int64 | Float64 | String | Array(Any) | Hash(Any, Any)\n def !=(other : Any) : Bool\n # Structural: same kind, same value; a mapping equals a mapping with the\n # same pairs in any order. `1` and `1.0` are different values.\n def ==(other : Any) : Bool\n def [](key : Any) : Any\n def [](key : String) : Any\n # An Int32 indexes a sequence and reaches an integer key of a mapping:\n # `1: one` has the `Int64` key 1, and `doc[1]` panicked \"expected a\n # sequence, not a mapping\" while `[]?` and `dig?` answered nil.\n def [](index : Int32) : Any\n def []?(key : Any) : Any | ::Nil\n def []?(key : String) : Any | ::Nil\n def []?(index : Int32) : Any | ::Nil\n def as_a : Array(Any)\n def as_a? : Array(Any) | ::Nil\n def as_bool : Bool\n def as_bool? : Bool | ::Nil\n # A float, or an integer widened to one.\n def as_f : Float64\n def as_f? : Float64 | ::Nil\n def as_h : Hash(Any, Any)\n def as_h? : Hash(Any, Any) | ::Nil\n def as_i : Int64\n def as_i? : Int64 | ::Nil\n def as_nil : Nil\n def as_nil? : Nil\n def as_s : String\n def as_s? : String | ::Nil\n def dig(key : String | Int32) : Any\n def dig(key : String | Int32, *rest : String | Int32) : Any\n def dig?(key : String | Int32) : Any | ::Nil\n def dig?(key : String | Int32, *rest : String | Int32) : Any | ::Nil\n # Agrees with `==`: the same kind and value hash the same, a mapping's\n # hash does not depend on its order, and `0.0`/`-0.0` share one. A\n # sequence mixes its items as an `Array` does, a mapping sums one mixed\n # tuple hash per pair, and an integer hashes as an `Int64`. `31 * h +\n # item`, a sum of `31 * key + value` and the integer's own low bits\n # gave a 150 by 150 grid of `[i, j]` 4,769 hashes and of `{x: i, y: j}`\n # 299, `{a: 1, b: 2}` the hash of `{a: 2, b: 1}` and -1 that of 0, and\n # 40,000 such mappings took 987 ms to insert as keys; they take 19 ms.\n def hash : Int32\n def initialize(raw : Type)\n def initialize(value : Int32)\n def inspect : String\n # Which member of the union `raw` is, as one of the KIND_ constants.\n # Reference types are tested first on purpose (see the header).\n def kind : Int32\n # The name of the kind, for the sentences below.\n def kind_name : String\n def null? : Bool\n def raw : Type\n # Elements of a sequence, pairs of a mapping, characters of a string.\n def size : Int32\n # A string as itself, a number or boolean as written, nil as nothing,\n # a collection the way the prelude prints an array or a hash.\n def to_s : String\nend\n\n# Reads one stream. Every failure is recorded once in `@error` and every\n# loop returns as soon as one is recorded; nothing is raised inside.\npub class Parser\n def error : String | ::Nil\n def initialize(text : String)\n # Every document in the stream, or nil with `error` set.\n def parse_all : Array(Any) | ::Nil\nend\n\n# A type that can write itself as an `Any`. `YAML.to_yaml` and\n# `YAML.to_any` accept any value whose type implements it, as a `derive\n# yaml_serializable` type does.\npub trait ToYAML\n abstract def to_yaml_any : Any\nend\n\npub struct YAML\n # Block YAML for `value`, ending in a line break.\n def self.dump(value : Any) : String\n # The document `text` holds, read into `type`: `Nil`, `Bool`, `Int32`,\n # `Int64`, `UInt8`, `UInt64`, `Float64`, `String`, `Any`, a type with\n # `derive yaml_serializable`, one of those or `nil`, or an `Array` or a\n # `Hash` of those. Text that is not YAML panics as `parse` does; a value\n # of another kind, or an integer outside the type, is refused.\n def self.from_yaml(text : String, type : T.class) : T forall T\n def initialize\n def self.parse(text : String) : Any\n # The first document of `text`, or nil when the stream is not YAML this\n # module reads (the whole stream is checked, not just the first\n # document). An empty stream is a null document.\n def self.parse?(text : String) : Any | ::Nil\n def self.parse_all(text : String) : Array(Any)\n def self.parse_all?(text : String) : Array(Any) | ::Nil\n def self.read_value(value : Any, type : Nil.class) : Nil\n def self.read_value(value : Any, type : Bool.class) : Bool\n def self.read_value(value : Any, type : Int32.class) : Int32\n def self.read_value(value : Any, type : Int64.class) : Int64\n def self.read_value(value : Any, type : UInt8.class) : UInt8\n def self.read_value(value : Any, type : UInt64.class) : UInt64\n # A float, or an integer widened to one: `y: 2` is the Float64 2.0.\n def self.read_value(value : Any, type : Float64.class) : Float64\n def self.read_value(value : Any, type : String.class) : String\n def self.read_value(value : Any, type : Any.class) : Any\n def self.read_value(value : Any, type : Array(T).class) : Array(T) forall T\n def self.read_value(value : Any, type : Hash(K, V).class) : Hash(K, V) forall K, V\n # A type that reads itself, the `initialize(yaml)` a `derive\n # yaml_serializable` wrote; a type that admits nil reads null as the nil\n # and anything else as the other type, as std/json's `read_value` does.\n def self.read_value(value : Any, type : T.class) : T forall T\n # The `Any` a value is written as: the value or `nil`, the way\n # `JSON.to_json` takes it. A nil is taken out here, before the rest\n # dispatches, for the reason std/json's `write_or_null` gives: handed to\n # `write_value` whole, `Int32 | Nil` matches the `ToYAML` overload as\n # one type and does not build.\n def self.to_any(val : T) : Any forall T\n # Block YAML for any value `to_any` knows, written as `dump` writes it.\n def self.to_yaml(obj : T) : String forall T\n def self.write_value(val : Nil) : Any\n def self.write_value(val : Bool) : Any\n def self.write_value(val : Int32) : Any\n def self.write_value(val : Int64) : Any\n def self.write_value(val : UInt8) : Any\n # A YAML integer is read as an `Int64`, and one past it as a float, so a\n # `UInt64` above 9223372036854775807 would read back as a different\n # number: it is refused rather than written.\n def self.write_value(val : UInt64) : Any\n def self.write_value(val : Float64) : Any\n def self.write_value(val : String) : Any\n def self.write_value(val : Any) : Any\n def self.write_value(val : Array(T)) : Any forall T\n # A key of any type `to_any` writes as a scalar: YAML keys are typed,\n # so `Hash(Int32, String)` is written with integer keys and reads back\n # as itself. A collection key is refused by `dump`.\n def self.write_value(val : Hash(K, V)) : Any forall K, V\n def self.write_value(val : T) : Any forall T : ToYAML\nend\n" }, { "name": "std/zip", "path": "src/std/zip.iyi", - "sourceSha256": "3eebb3cf759efe99411bb2488c3cfd7c1e181eceaabc8807e010cb3a8d55c8e3", + "sourceSha256": "35ecdadd25759b58bfd16e27e1800a020672a28abf7126f4958a8f4e6b25108c", "surface": "module std/zip\n\n# One entry of the central directory.\npub class ZipEntry\n def comment : String\n def compressed_size : Int64\n def crc32 : UInt32\n # Bit 3: the CRC-32 and sizes follow the data, in a data descriptor.\n def descriptor? : Bool\n def directory? : Bool\n # Bit 0: the data is encrypted, which this module does not read.\n def encrypted? : Bool\n # The general purpose bit flags.\n def flags : Int32\n def initialize(name : String, raw_name : String, size : Int64, compressed_size : Int64, crc32 : UInt32, method : Int32, flags : Int32, modified : Time, offset : Int64, comment : String, zip64 : Bool)\n # The method number: 0 stored, 8 deflate, 12 bzip2, 14 LZMA, 93\n # Zstandard, 95 xz; any other is listed and refused when the entry is\n # opened.\n def method : Int32\n # The method's name, for a sentence about it.\n def method_name : String\n def modified : Time\n # The name as the archive's directory holds it, decoded to UTF-8.\n def name : String\n # Where the entry's local header is, from the start of the archive.\n def offset : Int64\n # The name's bytes as written, before decoding.\n def raw_name : String\n # The uncompressed size.\n def size : Int64\n # Bit 11: the name is UTF-8 rather than code page 437.\n def utf8? : Bool\n # Whether the central directory carried this entry's sizes or offset\n # in a ZIP64 extended information field.\n def zip64? : Bool\nend\n\n# One entry's content, read a piece at a time; `ZipReader#open` makes\n# them. The CRC-32 and the size are checked against the central\n# directory before the last piece is handed out, so a caller that stops\n# at the size it was told has still had every byte checked; and content\n# that inflates past the size the directory declares is refused at the\n# read that would pass it.\n#\n# Each method's decoder is the module's that has it: `Inflater` from\n# std/compress, `Bzip2Reader` from std/bzip2, `ZstdReader` from std/zstd,\n# and `XzReader` from std/xz for xz and, as a .lzma file, for LZMA. The\n# window a Zstandard frame names may be up to 512 MiB, the most\n# `ZstdReader` takes, and the dictionary an LZMA or xz stream names up\n# to `XzReader::DICT_MAX`, rather than their own defaults: both readers\n# grow their buffers with the output, not to the window, and the output\n# is held to the size the directory declares - which `ZipReader.open`'s\n# limit bounds - so the memory an entry costs is its own size at most,\n# and an entry another tool wrote with a large window is read.\npub class ZipEntryReader\n # Whether every byte has been read and checked.\n def finished? : Bool\n # *lzma_head* is the .lzma header `ZipReader#open` made of an LZMA\n # entry's own, which the decoder is given before the data after it.\n def initialize(source : ZipSource, entry : ZipEntry, data_start : Int64, slot_end : Int64, lzma_head : String)\n # Up to *max* bytes of the content, \"\" at its end; or what is wrong\n # with it, after which every read answers the same. A *max* below 1 is\n # a panic.\n def read(max : Int32 = 65536) : String | ZipError\nend\n\n# What is wrong with an archive or an entry: `message` is the sentence,\n# which says which entry where there is one.\npub struct ZipError\n def initialize(message : String)\n def to_s : String\nend\n\n# The methods this module writes, by their number in the format.\npub enum ZipMethod : Int32\n Stored = 0\n Deflated = 8\n Bzip2 = 12\n Zstandard = 93\nend\n\n# An archive's entries, read from its central directory.\n#\n# ```\n# zip = ZipReader.open(File.read(\"a.zip\"))!\n# zip.entries.each { |e| puts \"#{e.name} #{e.size}\" }\n# if readme = zip.find(\"README\")\n# text = zip.read(readme)!\n# end\n# ```\npub class ZipReader\n # The archive's comment, its bytes as written.\n def comment : String\n def entries : Array(ZipEntry)\n # Every entry written under *dir*, directories made as needed, files\n # that are there already written over. Every name is checked before\n # anything is written: one that is absolute, has a `..` part, or holds\n # a `\\` or a `:` (a separator and a drive on Windows) would land outside\n # *dir*, and the archive is refused whole. A file whose content fails\n # its check is deleted and the error answered.\n def extract(dir : String) : Nil | ZipError\n # The entry named *name*, or nil.\n def find(name : String) : ZipEntry | ::Nil\n # *entry*'s content as a stream; see `ZipEntryReader`. An encrypted\n # entry, one in a method this module does not read, and one whose\n # local header is not where and what the directory says are refused,\n # and so is an LZMA entry whose own header is not LZMA's.\n def open(entry : ZipEntry) : ZipEntryReader | ZipError\n # The archive in *data*. *limit* is the most the entries may hold\n # uncompressed, all together: an archive whose directory says more is\n # refused here, and an entry that inflates past what the directory says\n # it holds is refused as it reads.\n def self.open(data : String, limit : Int64 = NO_LIMIT) : ZipReader | ZipError\n # The archive in a file open for reading, which is read where the\n # directory and the entries are and not whole. It is moved about with\n # `seek`; the reader and its entry readers own its position.\n def self.open(io : IyiIO, limit : Int64 = NO_LIMIT) : ZipReader | ZipError\n # *entry*'s whole content, checked as `open`'s stream checks it.\n def read(entry : ZipEntry) : String | ZipError\n # Why *name* may not be written under a directory, or nil when it may.\n def self.unsafe_name(name : String) : String | ::Nil\nend\n\n# An archive written to an `IyiIO`, an entry at a time, the central\n# directory at `finish`.\n#\n# ```\n# File.open(\"out.zip\", \"w\") do |io|\n# zip = ZipWriter.new(io)\n# zip.add(\"README\", text)\n# zip.add(\"photo.jpg\", jpeg, ZipMethod::Stored)\n# zip.add_directory(\"empty\")\n# zip.finish\n# end\n# ```\n#\n# What is wrong with the calls is a panic: a name that is empty, longer\n# than 65,535 bytes, absolute or holding a `\\` (APPNOTE 4.4.17), or\n# already in the archive; an `add` after `finish`, and `finish` twice; a\n# comment longer than 65,535 bytes; bzip2 or Zstandard asked of an entry\n# streamed from an `IyiIO`, which those two compress only whole.\npub class ZipWriter\n # *data* as the entry *name*, compressed with *method* - deflate,\n # bzip2 (blocks of 900,000 bytes, bzip2's -9, as Python's `zipfile`\n # writes it), Zstandard, or stored as it is - and modified at\n # *modified*'s wall clock.\n def add(name : String, data : String, method : ZipMethod = ZipMethod::Deflated, modified : Time = Time.local) : Nil\n # What *source* holds until its end as the entry *name*, written as it\n # is read: the local header first, without the sizes and CRC-32, and a\n # data descriptor after the data with them. *source* is not closed.\n # Deflated or stored only: the bzip2 and Zstandard compressors take\n # their input whole, and an entry for them is added from a `String`.\n def add(name : String, source : IyiIO, method : ZipMethod = ZipMethod::Deflated, modified : Time = Time.local) : Nil\n # A directory entry: *name* with a `/` after it if it has none, stored\n # and empty.\n def add_directory(name : String, modified : Time = Time.local) : Nil\n # The central directory and the end, with *comment*; then *io* is\n # flushed. Once only.\n def finish(comment : String = \"\") : Nil\n # *zip64* writes every entry with the ZIP64 extended field and the end\n # with the ZIP64 records, needed or not. Offsets count from where *io*\n # is when the writer starts.\n def initialize(io : IyiIO, zip64 : Bool = false)\nend\n\nimpl Error for ZipError\n def message : String\nend\n" }, { "name": "std/zstd", "path": "src/std/zstd.iyi", - "sourceSha256": "421c98767f26a59497e71ae97c8682f402e443934577c39b5b73903da51fec83", + "sourceSha256": "476e94a9ad53ca23ed2a217ce4af4002bed7603a84f2c03cb32a39c2db6a460d", "surface": "module std/zstd\n\npub struct Zstd\n # One frame of *data*, with its content size and checksum, that any\n # Zstandard decoder reads with a window of 1 MiB at most.\n def self.compress(data : String) : String\n # Every frame of *data*, one after another, or the error that stopped\n # it. Bytes after the last frame that do not begin another are refused,\n # as is a stream with no frame at all.\n def self.decompress(data : String, max_window : Int32 = WINDOW_LIMIT) : String | ZstdError\n def initialize\nend\n\n# What `ZstdReader#read` and `Zstd.decompress` answer for a stream they\n# cannot read: one that is corrupt, that ends early, whose checksum or\n# content size does not match, or that asks for what this decoder does\n# not give (a dictionary, a window past the caller's limit).\npub struct ZstdError\n def initialize(message : String)\n def to_s : String\nend\n\n# A Zstandard stream read a piece at a time: the compressed bytes come\n# from the block, the decompressed ones go out through `read`, and memory\n# stays at the frame's window, twice over at most, and a block.\n#\n# ```\n# body = ZstdReader.new { socket_read }\n# while (piece = body.read(65536)!) != \"\"\n# ...\n# end\n# ```\npub class ZstdReader\n # Whether the stream has ended, its checksums checked, and every byte\n # been read: the next `read` answers \"\".\n def finished? : Bool\n # *source* answers the next bytes of the compressed stream, nil (or \"\")\n # when there are no more. *max_window* is the largest window a frame\n # may ask for; a frame that asks for more is refused rather than given\n # the memory. A limit outside 1 KiB to 512 MiB is a panic.\n def initialize(max_window : Int32 = Zstd::WINDOW_LIMIT, &source : (-> String | ::Nil))\n # Bytes the source gave beyond the last frame, for a caller that frames\n # other data after it; \"\" before the stream has ended.\n def leftover : String\n # Up to *max* decompressed bytes; \"\" once the last frame has ended and\n # been checked. A read answers what the last block decoded before it\n # decodes another, so it can answer fewer than *max* before the end, and\n # never more than a block (128 KiB).\n #\n # A corrupt stream, a bad checksum, a frame past the window limit or\n # input that ends early is a `ZstdError`, after which every read answers\n # the same error. A *max* below 1 is a panic.\n def read(max : Int32 = 65536) : String | ZstdError\nend\n\nimpl Error for ZstdError\n def message : String\nend\n" } ], @@ -720,15 +732,23 @@ "sources": [ { "path": "src/iyi/array.iyi", - "sha256": "743e8683780994c0c1c3e7bb927a8d178a77e94c4261c049a6ff976b2f0de894" + "sha256": "18d2214e0b08489dc739500cb4893ce92b3ba0b52151da497116babc639dd041" }, { "path": "src/iyi/atomic.iyi", - "sha256": "4ab1697b993ac0b43bb3d58b8e9be9e95fa2cea1900830a39626d9289cda370e" + "sha256": "b301ef3d70ffd132667c6a30a08cbc1bd097a53d7d54657f4c1fbcc077ba8f2b" + }, + { + "path": "src/iyi/chaos.iyi", + "sha256": "d3ccdc5b264e6e101a4909fbee4a9d5159eea843e74775a180f8872496bd2185" + }, + { + "path": "src/iyi/chaos_points.iyi", + "sha256": "76ca17181f046c69b2571bce0d4597025a7767c05c5c2aefa99606c70a949a19" }, { "path": "src/iyi/concurrency.iyi", - "sha256": "197f8f27f21e424be85d1f8b5d607659771202d51712f5304f7012125d44abd8" + "sha256": "021c684d3c3bdbd3c667353aa6c19c15a42d924a455e32fa2bce747496004201" }, { "path": "src/iyi/enum.iyi", @@ -736,19 +756,19 @@ }, { "path": "src/iyi/file.iyi", - "sha256": "431b8bf5a51054074315815806a66caf64c25ddc4b6b55e06dd53511f18f500b" + "sha256": "5b53e9d449aedbaaa54164d9367d15847f2053724230740ea08b087625a06346" }, { "path": "src/iyi/float.iyi", - "sha256": "9d80115e42794ed62904bdd85a60874f3b94f46c69ec585492b86e0f7e410006" + "sha256": "c3f7055712a46a3d09210d75d65d391a3f910b78d05a1fff2d6de7fa693696c4" }, { "path": "src/iyi/hash.iyi", - "sha256": "580ef1c3bc63419b8df4724b685a551166f02072a4e7b8fdbc0efab34de30167" + "sha256": "3e14e6d5a6504b2f275fe627f199361ed6323374d9b1bad30cc6c38217ae0ee6" }, { "path": "src/iyi/io.iyi", - "sha256": "268eeea54810c6949118bd362e108977e95c0c5021845e626d75d1d579617b9e" + "sha256": "555b36aecd3aee8308a754de5d3d9cb2cb5d1c85cccf86355c26cb55b3a06a95" }, { "path": "src/iyi/macros.iyi", @@ -756,7 +776,7 @@ }, { "path": "src/iyi/number.iyi", - "sha256": "4670aae20350cb7f1961bf7b4354d1824b76595598a855b50eb76e170874c24f" + "sha256": "0bc0c85ef6d9126f4a637f44789af54c65337c916ae99cfbf06659eff654b498" }, { "path": "src/iyi/object.iyi", @@ -764,11 +784,11 @@ }, { "path": "src/iyi/prelude.iyi", - "sha256": "2c724ca28f93ee1260c649727ed33f201960b04ca64108a0e3fb07d7a52f051e" + "sha256": "2f4d52f16e06e9be00df5639e0ae10168658d1bd72fc9c0b846e4ed76f98df07" }, { "path": "src/iyi/primitives.iyi", - "sha256": "3a47aad1995bba3bfd5b3f48db8fa6aaf92000e6caae19be58d4d4ecf1691a48" + "sha256": "55e4061004d37de68c63f02cb2667039fc1475e5ccf5b1d23bcc3a7c0a9e1e17" }, { "path": "src/iyi/range.iyi", @@ -780,7 +800,7 @@ }, { "path": "src/iyi/string.iyi", - "sha256": "bedeecee93e9b30cfe7f4410ab86cbbc77f0e85058baf668b506ff773561ebd8" + "sha256": "3c71a975a0b1d073d1625f570769fd1fc3e19a23dcf44d2c64ee2587efafafe6" }, { "path": "src/iyi/testing.iyi", @@ -788,7 +808,7 @@ }, { "path": "src/iyi/thread.iyi", - "sha256": "b64bff5faeafcb16053081d50199130cf93fff184ace18c3505e80dee6a34b40" + "sha256": "67330d48758d307e94c7c48828278ba94d56d9ca43ea7c69a28dd922b9c7f790" } ], "types": [ @@ -810,7 +830,7 @@ }, { "name": "Channel", - "surface": "# ── Channel — III.4.6 ──────────────────────────────────────────────────\n#\n# `Channel(T).new` is a rendezvous, as Crystal's is: a send completes when\n# a receive takes it, and either side parks until the other arrives. A\n# parked sender carries its value in its node's box, and a parked\n# receiver has a slot a sender fills, so the handoff needs no third\n# party; `new(capacity)` buffers that many sends first. Closing drains\n# what was delivered, then refuses: a parked sender whose box is still\n# full failed, and its value dies with the node.\n#\n# A channel crosses threads (SPEC.md III.4.13): it is `@[Share]`,\n# trusted whenever `T` is - III.4.6's \"`Channel(T)` carries over, with\n# `T : Share`\" - so a parallel task and a thread may capture one whose\n# values are themselves safe on two threads, and every operation below\n# is behind the scheduler's lock, which a park lets go of before it\n# switches. A wake of a fiber on another thread goes through its inbox\n# (`IyiScheduler.ready`). `select` is answered by this protocol — a node\n# per arm, one nonce, losers stale by arithmetic until the woken select\n# takes them out — at the bottom of the class.\nclass Channel(T)\n\n # Everyone parked is woken; what each wake means is read on the woken\n # side — a receiver finds the buffer, then `ChannelClosed`; a sender\n # finds its box still full.\n def close : Nil\n\n def closed? : Bool\n\n def new(capacity : Int32)\n\n def new\n\n # `select`'s arms park through these with one shared nonce; a plain\n # call parks with a fresh nonce of its own. *slot* is where a rendezvous\n # sender puts its value (`IyiWaiter#slot`). These and the two unlinks\n # below are public for the arms' sake and change the queues, so like\n # the `*_held` halves they are called with the scheduler's lock held.\n def park_receive_node(fiber : IyiFiber, nonce : UInt64, slot : UInt64) : IyiWaiter\n\n def park_send_node(fiber : IyiFiber, nonce : UInt64, value : T) : IyiSendNode(T)\n\n # :nodoc: - the scheduler's lock held, and `send_ready_held?` true. A\n # rendezvous puts the value in the parked receiver's own slot (`hand`).\n # It went into the buffer, and the send answered delivered on the word\n # of the wake: a select woken that way took another arm ready by then, a\n # receiver cancelled before it ran answered `Cancelled`, and either way\n # the value stayed in a channel of no capacity after its sender left. A\n # buffered send completes when the buffer takes it, so there the woken\n # receiver finds it in the buffer.\n def put_held(value : T) : Nil | ChannelClosed\n\n # The value sent without waiting: to a parked receiver, or into the\n # buffer; `ChannelClosed` once the channel is. A full channel has no\n # room, and asking is a bug the call panics over; `select` with an\n # `else` arm is the send that may not happen.\n def put_now(value : T) : Nil | ChannelClosed\n\n # A closed channel drains what was delivered before it refuses. A\n # rendezvous sender that finds this receive parked writes its value into\n # `got` and marks the node handed, and that is the answer even when the\n # task was cancelled before it ran: the sender was told it was taken.\n # A woken park that was handed nothing takes its node out of the queue,\n # so a cancelled one leaves nothing behind.\n def receive : T | ChannelClosed | Cancelled\n\n def receive_ready? : Bool\n\n # :nodoc: - the scheduler's lock held.\n def receive_ready_held? : Bool\n\n # Nothing happens on the arm `take_held` answers: `ChannelClosed` is\n # the refusal here, where a closed channel's buffer is spent.\n def receive_refused? : Bool\n\n def receive_select_action : IyiReceiveSelect(T)\n\n def send(value : T) : Nil | ChannelClosed | Cancelled\n\n def send_ready? : Bool\n\n # :nodoc: - the scheduler's lock held.\n def send_ready_held? : Bool\n\n def send_select_action(value : T) : IyiSendSelect(T)\n\n # :nodoc: - the scheduler's lock held, and `receive_ready_held?` true:\n # the buffer, then a parked sender's box, then the refusal.\n def take_held : T | ChannelClosed\n\n # The next value without waiting: the buffer, then a parked sender's\n # box, then `ChannelClosed`. On an empty open channel there is nothing\n # to take, and asking is a bug the call panics over; `select` with an\n # `else` arm is the receive that may find nothing.\n def take_now : T | ChannelClosed\n\n # Out of the receive queue from wherever it stands, in constant time,\n # and no longer linked anywhere; a node no queue holds is left as it\n # is. The queue is linked both ways because a select's losing arms\n # leave from the middle: they stayed until something walked them off\n # the head, so a loop selecting over a busy channel and an idle one\n # left a node in the idle one's queue for every park - 30 MiB kept\n # after two million rounds, freed only when the idle channel was used.\n def unlink_receiver(node : IyiWaiter) : Nil\n\n # The send queue's `unlink_receiver`.\n def unlink_sender(node : IyiSendNode(T)) : Nil\n\n # No arm ready answers -1, which the expansion's case meets with the\n # program's own `else`.\n def self.non_blocking_select(actions : U, bound : Int32) forall U\n\n # ── select — III.4.6, the library half of the expansion ────────────\n #\n # A `select` compiles to `%i, %v, %h = ::Channel.select({arm, arm}, b)`\n # and a case over the index; `non_blocking_select` when an `else` arm\n # exists. One def serves every arity, because a tuple's size is a\n # compile-time fact a macro walks. `%h` says the arm's operation\n # happened, and an arm nobody binds runs its body only then: it ran\n # unless its answer was an `Error`, so a `Channel(Boom)`'s receive took\n # the `Boom` and skipped the body. *bound* is the first arm that binds,\n # -1 for none, and a cancelled task's `Cancelled` goes to it, as that\n # arm's own primitive would have answered it.\n #\n # Every look and every act is under the scheduler's lock, one lock for\n # every channel an arm names, so no arm's channel changes between the\n # look that found it ready and the act; the park lets go of it.\n def self.select(actions : U, bound : Int32) forall U\nend\n" + "surface": "# ── Channel — III.4.6 ──────────────────────────────────────────────────\n#\n# `Channel(T).new` is a rendezvous, as Crystal's is: a send completes when\n# a receive takes it, and either side parks until the other arrives. A\n# parked sender carries its value in its node's box, and a parked\n# receiver has a slot a sender fills, so the handoff needs no third\n# party; `new(capacity)` buffers that many sends first. Closing drains\n# what was delivered, then refuses: a parked sender whose box is still\n# full failed, and its value dies with the node.\n#\n# A channel crosses threads (SPEC.md III.4.13): it is `@[Share]`,\n# trusted whenever `T` is - III.4.6's \"`Channel(T)` carries over, with\n# `T : Share`\" - so a parallel task and a thread may capture one whose\n# values are themselves safe on two threads, and every operation below\n# is behind the channel's own lock (`IyiChanLock`), which a park lets go\n# of before it switches. A woken fiber's park is claimed first\n# (`IyiWaiter#claim`), since a select parked here may be woken by another\n# channel's waker at the same moment, and a node whose claim fails is\n# stale. A wake of a fiber on another thread goes through its inbox\n# (`IyiScheduler.ready`). `select` is answered by this protocol — a node\n# per arm, one nonce, losers stale once one wake is claimed, until the\n# woken select takes them out — at the bottom of the class.\nclass Channel(T)\n\n # Everyone parked is woken; what each wake means is read on the woken\n # side — a receiver finds the buffer, then `ChannelClosed`; a sender\n # finds its box still full.\n def close : Nil\n\n def closed? : Bool\n\n # :nodoc: - the lock's word, by address: what `select` orders its\n # channels' locks by.\n def lock_address : UInt64\n\n def new(capacity : Int32)\n\n def new\n\n # `select`'s arms park through these with one shared nonce; a plain\n # call parks with a fresh nonce of its own. *slot* is where a rendezvous\n # sender puts its value (`IyiWaiter#slot`). These and the two unlinks\n # below are public for the arms' sake and change the queues, so like\n # the `*_held` halves they are called with the channel's lock held.\n def park_receive_node(fiber : IyiFiber, nonce : UInt64, slot : UInt64) : IyiWaiter\n\n def park_send_node(fiber : IyiFiber, nonce : UInt64, value : T) : IyiSendNode(T)\n\n # :nodoc: - the channel's lock held; `IyiMissed` when a send would\n # park. A rendezvous puts the value in the parked receiver's own slot\n # (`hand`). It went into the buffer, and the send answered delivered on\n # the word of the wake: a select woken that way took another arm ready\n # by then, a receiver cancelled before it ran answered `Cancelled`, and\n # either way the value stayed in a channel of no capacity after its\n # sender left. A buffered send completes when the buffer takes it, so\n # there the woken receiver finds it in the buffer.\n def put_held(value : T) : Nil | ChannelClosed | IyiMissed\n\n # The value sent without waiting: to a parked receiver, or into the\n # buffer; `ChannelClosed` once the channel is. A full channel has no\n # room, and asking is a bug the call panics over; `select` with an\n # `else` arm is the send that may not happen.\n def put_now(value : T) : Nil | ChannelClosed\n\n # A closed channel drains what was delivered before it refuses. A\n # rendezvous sender that finds this receive parked writes its value into\n # `got` and marks the node handed, and that is the answer even when the\n # task was cancelled before it ran: the sender was told it was taken.\n # A woken park that was handed nothing takes its node out of the queue,\n # so a cancelled one leaves nothing behind.\n def receive : T | ChannelClosed | Cancelled\n\n # Whether a receive would not park now: an answer that may be old by\n # the time it is read.\n def receive_ready? : Bool\n\n # Nothing happens on the arm `take_held` answers: `ChannelClosed` is\n # the refusal here, where a closed channel's buffer is spent.\n def receive_refused? : Bool\n\n def receive_select_action : IyiReceiveSelect(T)\n\n def send(value : T) : Nil | ChannelClosed | Cancelled\n\n # Whether a send would not park now: an answer that may be old by the\n # time it is read.\n def send_ready? : Bool\n\n def send_select_action(value : T) : IyiSendSelect(T)\n\n # :nodoc: - the channel's lock held: the buffer, then a parked sender's\n # box, then the refusal, or `IyiMissed` when a receive would park.\n def take_held : T | ChannelClosed | IyiMissed\n\n # The next value without waiting: the buffer, then a parked sender's\n # box, then `ChannelClosed`. On an empty open channel there is nothing\n # to take, and asking is a bug the call panics over; `select` with an\n # `else` arm is the receive that may find nothing.\n def take_now : T | ChannelClosed\n\n # :nodoc: - the lock held: the fiber the last operation claimed, taken\n # out to be readied once the lock is let go (`wake`).\n def take_woken : IyiFiber | ::Nil\n\n # Out of the receive queue from wherever it stands, in constant time,\n # and no longer linked anywhere; a node no queue holds is left as it\n # is. The queue is linked both ways because a select's losing arms\n # leave from the middle: they stayed until something walked them off\n # the head, so a loop selecting over a busy channel and an idle one\n # left a node in the idle one's queue for every park - 30 MiB kept\n # after two million rounds, freed only when the idle channel was used.\n def unlink_receiver(node : IyiWaiter) : Nil\n\n # The send queue's `unlink_receiver`.\n def unlink_sender(node : IyiSendNode(T)) : Nil\n\n # No arm ready answers -1, which the expansion's case meets with the\n # program's own `else`.\n def self.non_blocking_select(actions : U, bound : Int32) forall U\n\n # ── select — III.4.6, the library half of the expansion ────────────\n #\n # A `select` compiles to `%i, %v, %h = ::Channel.select({arm, arm}, b)`\n # and a case over the index; `non_blocking_select` when an `else` arm\n # exists. One def serves every arity, because a tuple's size is a\n # compile-time fact a macro walks. `%h` says the arm's operation\n # happened, and an arm nobody binds runs its body only then: it ran\n # unless its answer was an `Error`, so a `Channel(Boom)`'s receive took\n # the `Boom` and skipped the body. *bound* is the first arm that binds,\n # -1 for none, and a cancelled task's `Cancelled` goes to it, as that\n # arm's own primitive would have answered it.\n #\n # Every look and every act is under the locks of every channel an arm\n # names, taken in the order of their addresses (`IyiChanLock.order`),\n # so no arm's channel changes between the look and the act, nor while\n # the arms park; the park lets go of them all.\n def self.select(actions : U, bound : Int32) forall U\n\n # :nodoc: - what `take_woken` answered, readied with no lock held.\n def self.wake(woken : IyiFiber | ::Nil) : Nil\nend\n" }, { "name": "ChannelClosed", @@ -862,7 +882,7 @@ }, { "name": "Pointer", - "surface": "# A raw address: `Pointer(T).malloc(n)` is `n` elements of untyped memory,\n# `[]` and `[]=` read and write them, nothing checks the bounds.\nstruct Pointer(T) < Value\n\n def +(offset : Int64) : self\n\n def -(other : self) : Int64\n\n def [](offset : Int32) : T\n\n def []=(offset : Int32, value : T) : T\n\n def address : UInt64\n\n # Bytes, not elements: every caller here is copying string contents. A\n # million a run (`copy_run`, prelude.iyi), and a check for preemption\n # between runs (SPEC.md III.4.13, part 9); one run or less, none.\n def copy_from(source : Pointer(T), count : Int32) : Nil\n\n def copy_run(source : Pointer(T), count : Int32) : Nil\n\n def new\n\n def realloc(size : UInt64) : self\n\n def value : T\n\n def value=(value : T)\n\n def self.malloc(size : UInt64)\n\n def self.new(address : UInt64)\nend\n" + "surface": "# A raw address: `Pointer(T).malloc(n)` is `n` elements of untyped memory,\n# `[]` and `[]=` read and write them, nothing checks the bounds.\nstruct Pointer(T) < Value\n\n def +(offset : Int64) : self\n\n def -(other : self) : Int64\n\n # The same address, whatever is stored there. Without it `Object#==`\n # answered false for `p == p`, and a pointer key was never found.\n def ==(other : self) : Bool\n\n def [](offset : Int32) : T\n\n def []=(offset : Int32, value : T) : T\n\n def address : UInt64\n\n # Bytes, not elements: every caller here is copying string contents. A\n # million a run (`copy_run`, prelude.iyi), and a check for preemption\n # between runs (SPEC.md III.4.13, part 9); one run or less, none.\n def copy_from(source : Pointer(T), count : Int32) : Nil\n\n def copy_run(source : Pointer(T), count : Int32) : Nil\n\n def new\n\n def realloc(size : UInt64) : self\n\n def value : T\n\n def value=(value : T)\n\n def self.malloc(size : UInt64)\n\n def self.new(address : UInt64)\nend\n" }, { "name": "Proc", @@ -886,7 +906,7 @@ }, { "name": "String", - "surface": "# Text — SPEC.md, \"0.1.0\", item 3.\n#\n# The compiler declares `String`'s fields itself — `@bytesize`, `@length` and\n# `@c`, in that order, in `Program#initialize` — because codegen lays out\n# string literals before any prelude is read. So this file reopens a type that\n# already has its shape and only says what can be done with it.\nclass String\n\n # `\"ab\" * -3` answered `\"\"` where the other library raises (III.1.7a).\n # The size is a division, so the refusal names the string: `\"abcd\" *\n # 600_000_000` panicked \"arithmetic overflow\", the machine's adder.\n def *(count : Int32) : String\n\n def +(other : String) : String\n\n # Byte order, which for ASCII is dictionary order with the capitals first;\n # a prefix sorts before what it prefixes. What `sorted` and `sorted_by`\n # ask of a string.\n def <(other : String) : Bool\n\n # Thirty-two bytes a step while they last, then one at a time, and the\n # same string is itself: a byte a step was eleven times Python's `==`,\n # 230 ms for 1,000 compares of equal megabytes. No load needs alignment.\n def ==(other : String) : Bool\n\n # A negative *start* counts from the end, the same wrap as the one-index\n # form. *count* stays non-negative: a negative count is not a wrap, it is a\n # mistake, and it raises for that reason rather than being silently clamped.\n def [](start : Int32, count : Int32) : String\n\n # The character at *index*, asked for by `samples/iyi/calc`, which scans\n # its input a character at a time. Byte-indexed: a byte is the character\n # for ASCII, and `each_char` is the decoded walk. A negative index counts\n # from the end; out of range after that wrap raises, `[]?` answers nil.\n def [](index : Int32) : Char\n\n def []?(index : Int32) : Char | ::Nil\n\n # Where *search* first starts at or after byte *offset* - counted from\n # the end when negative - in bytes, as Crystal's `byte_index` answers.\n # Naive while that is cheap, and Rabin-Karp - one rolling hash per\n # position, bytes compared only where it matches - once more than four\n # bytes a position have been compared: a needle that keeps almost\n # matching, `\"a\" * 1000 + \"b\"` in a run of `a`, made the naive loop\n # n * m, 222 ms for a megabyte.\n def byte_index(search : String, offset : Int32 = 0) : Int32 | ::Nil\n\n def bytesize : Int32\n\n # `'\\n'` is a line's end, so it takes the `\\r` before it, or a lone `\\r`\n # in its place, as the other library's does: `\"a\\r\\n\".chomp('\\n')` is \"a\".\n def chomp(char : Char) : String\n\n # The other half of `upcase`, ASCII for the same reason.\n def downcase : String\n\n # Every character in order, decoded, as many as `size` counts: it yielded\n # one per byte, so `\"héllo\".size` was 5 and `each_char` ran six times.\n # Indexing stays byte-indexed (`[]` above), as `samples/iyi/calc` scans.\n # A byte that begins no character is one U+FFFD, as in the other library:\n # a lead byte took the bytes after it whatever they were, C3 41 was the\n # one character U+00C1, and `\"a\" C3 '\"'` swallowed the quote.\n def each_char(& : (Char -> Nil)) : Nil\n\n def empty? : Bool\n\n def ends_with?(suffix : String) : Bool\n\n # FNV-1a over the bytes: three lines, no table, and good enough for a\n # linear-probing table of a few dozen entries.\n def hash : Int32\n\n def includes?(other : String) : Bool\n\n # Where a character first is, or nil: the question a parser asks before\n # it cuts.\n def index(char : Char) : Int32 | ::Nil\n\n # The literal that spells it, escaped as the other library's, a byte that\n # is no UTF-8 as `\\x`: unescaped, `[\"a\\\", \\\"b\"]` printed as two strings.\n def inspect : String\n\n def lchop(char : Char) : String\n\n # The lines of a text, blank ones included, a final newline ending the last\n # line; `\\r\\n` ends one too, and a last `\\r` with no `\\n` after it is kept:\n # `\"1\\r\\n\".lines[0].to_i` panicked where `File.read_lines` read 1.\n def lines : Array(String)\n\n def ljust(width : Int32, pad : Char = ' ') : String\n\n # Pad on the left (rjust) or the right (ljust) to *width*, in characters\n # as `size` counts them, the pad as its own bytes: in bytes,\n # `\"héllo\".rjust(7, '.')` answered a string of `size` 6, and a `'·'`\n # written as one byte left a lone continuation byte in the result.\n def rjust(width : Int32, pad : Char = ' ') : String\n\n # A zero `@length` on a non-empty string is \"not counted yet\", which is\n # how the other library's own code leaves a string it builds: `to_json`'s\n # result answered `size` 0 here while the field was returned as it stood.\n # The layout is shared on purpose (SPEC.md Part V item 12), so the\n # invariant is too. The count is of what `each_char` yields, a byte that\n # begins no character one U+FFFD.\n def size : Int32\n\n # The pieces between separators, empty ones included: `\"a,,b\".split(',')`\n # is three, `\"a,\".split(',')` is two, and `\"\".split(',')` is one empty\n # piece - the count the other library gives, so a program reads the same\n # under both.\n def split(separator : Char) : Array(String)\n\n # The three the Kemal port's path joining needs, byte-indexed: the same\n # as by character while the strings are ASCII.\n def starts_with?(prefix : String) : Bool\n\n # A `Char` above ASCII is more than one byte, and these narrowed it to one\n # through a *checked* convert, so `\"a☃b\".index('☃')` died of \"arithmetic\n # overflow\". Each compares the character's own bytes now.\n def starts_with?(char : Char) : Bool\n\n # Without `Char#whitespace?`'s characters at either end, decoded: it read\n # bytes, and `\"b\\u00A0\".strip` kept the space the other library takes.\n def strip : String\n\n # The double nearest the decimal written here, correctly rounded: the\n # digits and the exponent are read into the printer's bignum and the\n # quotient taken to 54 bits with a sticky remainder, so what `to_s`\n # wrote reads back as the same double, every time, and \"0.1\" is the\n # double the literal `0.1` is. Accepts `-1.5`, `2.5e-3`, `1E10`, `.5`,\n # `7.`, `Infinity`, `NaN`, with `Char#whitespace?` around as `to_i?`\n # takes it; raises \"not a number\" for anything else.\n def to_f : Float64\n\n # The number this string spells - an optional sign and decimal digits,\n # the whole string - as `samples/iyi/calc` reads a token. Raises on\n # anything else, as the other library's does (SPEC.md III.1.7a): a\n # silent 0 for `\"oops\"` is wrong twice; `to_i?` answers nil for it.\n def to_i : Int32\n\n # Around the digits, `Char#whitespace?` is skipped as the other\n # library's `whitespace: true` skips it (`\"42\\n\".to_i?` was nil). Read\n # as a *negative* magnitude, which holds every value the type has:\n # `2147483648` accumulated and negated, `Int32::MIN` overflowed on its\n # last digit, a panic rather than the `nil` a `?` promises.\n def to_i? : Int32 | ::Nil\n\n def to_s : String\n\n def to_unsafe : Pointer(UInt8)\n\n # ASCII only, and the samples are ASCII. A prelude that folded case for\n # every alphabet would be a Unicode table, which is more lines than this\n # whole file and not what any sample is about.\n def upcase : String\n\n def self.build(capacity : Int32 = 64, & : (String::Builder -> _)) : String\n\n # Interpolation is a call the compiler writes: `\"a#{b}\"` reaches here as\n # `String.interpolation(\"a\", b)` (`LiteralExpander#expand`). The overloads\n # are what let the common shapes avoid work — a single string is itself.\n def self.interpolation(value : String) : String\n\n def self.interpolation(value) : String\n\n def self.interpolation(*values : *T) : String forall T\n\n # Allocates *bytesize* bytes, hands the buffer to the block to fill, and\n # returns the string that results. The character count is left at zero,\n # \"not counted yet\", for `size` to scan the first time it is asked: most\n # strings built here - an encoding, a join, a line - are never asked, and\n # counting them all was a second pass over every byte built.\n #\n # The byte after the last is a NUL, as a literal's is, so `to_unsafe` is\n # a C string: `std/process` and `std/debug` hand one to the platform as a\n # path. The byte was allocated for that and never written, and a string's\n # chunk is atomic - not cleared - so a chunk that had held a longer\n # string kept that string's byte there. Measured on Windows after a\n # collection that freed longer strings: 961 of 1,000 strings built after\n # it had no terminator, and `Process.run` of a path built that way\n # answered \"no such program\" for a file that was there.\n def self.new(bytesize : Int32, & : (Pointer(UInt8) -> Nil)) : String\nend\n" + "surface": "# Text — SPEC.md, \"0.1.0\", item 3.\n#\n# The compiler declares `String`'s fields itself — `@bytesize`, `@length` and\n# `@c`, in that order, in `Program#initialize` — because codegen lays out\n# string literals before any prelude is read. So this file reopens a type that\n# already has its shape and only says what can be done with it.\nclass String\n\n # `\"ab\" * -3` answered `\"\"` where the other library raises (III.1.7a).\n # The size is a division, so the refusal names the string: `\"abcd\" *\n # 600_000_000` panicked \"arithmetic overflow\", the machine's adder.\n def *(count : Int32) : String\n\n def +(other : String) : String\n\n # Byte order, which for ASCII is dictionary order with the capitals first;\n # a prefix sorts before what it prefixes. What `sorted` and `sorted_by`\n # ask of a string.\n def <(other : String) : Bool\n\n # Thirty-two bytes a step while they last, then one at a time, and the\n # same string is itself: a byte a step was eleven times Python's `==`,\n # 230 ms for 1,000 compares of equal megabytes. No load needs alignment.\n def ==(other : String) : Bool\n\n # A negative *start* counts from the end, the same wrap as the one-index\n # form. *count* stays non-negative: a negative count is not a wrap, it is a\n # mistake, and it raises for that reason rather than being silently clamped.\n def [](start : Int32, count : Int32) : String\n\n # The character at *index*, asked for by `samples/iyi/calc`, which scans\n # its input a character at a time. Byte-indexed: a byte is the character\n # for ASCII, and `each_char` is the decoded walk. A negative index counts\n # from the end; out of range after that wrap raises, `[]?` answers nil.\n def [](index : Int32) : Char\n\n def []?(index : Int32) : Char | ::Nil\n\n # The byte at *index*, checked: a negative index counts from the end, as\n # an array's does, and one out of range raises. What a module reads a\n # string's bytes with when it does not handle raw memory, where\n # `to_unsafe[i]` reads past the end without a word (SPEC.md III.13).\n def byte_at(index : Int32) : UInt8\n\n # Where *search* first starts at or after byte *offset* - counted from\n # the end when negative - in bytes, as Crystal's `byte_index` answers.\n # Naive while that is cheap, and Rabin-Karp - one rolling hash per\n # position, bytes compared only where it matches - once more than four\n # bytes a position have been compared: a needle that keeps almost\n # matching, `\"a\" * 1000 + \"b\"` in a run of `a`, made the naive loop\n # n * m, 222 ms for a megabyte.\n def byte_index(search : String, offset : Int32 = 0) : Int32 | ::Nil\n\n def bytesize : Int32\n\n # `'\\n'` is a line's end, so it takes the `\\r` before it, or a lone `\\r`\n # in its place, as the other library's does: `\"a\\r\\n\".chomp('\\n')` is \"a\".\n def chomp(char : Char) : String\n\n # The other half of `upcase`, ASCII for the same reason.\n def downcase : String\n\n # Every character in order, decoded, as many as `size` counts: it yielded\n # one per byte, so `\"héllo\".size` was 5 and `each_char` ran six times.\n # Indexing stays byte-indexed (`[]` above), as `samples/iyi/calc` scans.\n # A byte that begins no character is one U+FFFD, as in the other library:\n # a lead byte took the bytes after it whatever they were, C3 41 was the\n # one character U+00C1, and `\"a\" C3 '\"'` swallowed the quote.\n def each_char(& : (Char -> Nil)) : Nil\n\n def empty? : Bool\n\n def ends_with?(suffix : String) : Bool\n\n # FNV-1a over the bytes: three lines, no table, and good enough for a\n # linear-probing table of a few dozen entries.\n def hash : Int32\n\n def includes?(other : String) : Bool\n\n # Where a character first is, or nil: the question a parser asks before\n # it cuts.\n def index(char : Char) : Int32 | ::Nil\n\n # The literal that spells it, escaped as the other library's, a byte that\n # is no UTF-8 as `\\x`: unescaped, `[\"a\\\", \\\"b\"]` printed as two strings.\n def inspect : String\n\n def lchop(char : Char) : String\n\n # The lines of a text, blank ones included, a final newline ending the last\n # line; `\\r\\n` ends one too, and a last `\\r` with no `\\n` after it is kept:\n # `\"1\\r\\n\".lines[0].to_i` panicked where `File.read_lines` read 1.\n def lines : Array(String)\n\n def ljust(width : Int32, pad : Char = ' ') : String\n\n # Pad on the left (rjust) or the right (ljust) to *width*, in characters\n # as `size` counts them, the pad as its own bytes: in bytes,\n # `\"héllo\".rjust(7, '.')` answered a string of `size` 6, and a `'·'`\n # written as one byte left a lone continuation byte in the result.\n def rjust(width : Int32, pad : Char = ' ') : String\n\n # A zero `@length` on a non-empty string is \"not counted yet\", which is\n # how the other library's own code leaves a string it builds: `to_json`'s\n # result answered `size` 0 here while the field was returned as it stood.\n # The layout is shared on purpose (SPEC.md Part V item 12), so the\n # invariant is too. The count is of what `each_char` yields, a byte that\n # begins no character one U+FFFD.\n def size : Int32\n\n # The pieces between separators, empty ones included: `\"a,,b\".split(',')`\n # is three, `\"a,\".split(',')` is two, and `\"\".split(',')` is one empty\n # piece - the count the other library gives, so a program reads the same\n # under both.\n def split(separator : Char) : Array(String)\n\n # The three the Kemal port's path joining needs, byte-indexed: the same\n # as by character while the strings are ASCII.\n def starts_with?(prefix : String) : Bool\n\n # A `Char` above ASCII is more than one byte, and these narrowed it to one\n # through a *checked* convert, so `\"a☃b\".index('☃')` died of \"arithmetic\n # overflow\". Each compares the character's own bytes now.\n def starts_with?(char : Char) : Bool\n\n # Without `Char#whitespace?`'s characters at either end, decoded: it read\n # bytes, and `\"b\\u00A0\".strip` kept the space the other library takes.\n def strip : String\n\n # The double nearest the decimal written here, correctly rounded: the\n # digits and the exponent are read into the printer's bignum and the\n # quotient taken to 54 bits with a sticky remainder, so what `to_s`\n # wrote reads back as the same double, every time, and \"0.1\" is the\n # double the literal `0.1` is. Accepts `-1.5`, `2.5e-3`, `1E10`, `.5`,\n # `7.`, `Infinity`, `NaN`, with `Char#whitespace?` around as `to_i?`\n # takes it; raises \"not a number\" for anything else.\n def to_f : Float64\n\n # The number this string spells - an optional sign and decimal digits,\n # the whole string - as `samples/iyi/calc` reads a token. Raises on\n # anything else, as the other library's does (SPEC.md III.1.7a): a\n # silent 0 for `\"oops\"` is wrong twice; `to_i?` answers nil for it.\n def to_i : Int32\n\n # Around the digits, `Char#whitespace?` is skipped as the other\n # library's `whitespace: true` skips it (`\"42\\n\".to_i?` was nil). Read\n # as a *negative* magnitude, which holds every value the type has:\n # `2147483648` accumulated and negated, `Int32::MIN` overflowed on its\n # last digit, a panic rather than the `nil` a `?` promises.\n def to_i? : Int32 | ::Nil\n\n def to_s : String\n\n def to_unsafe : Pointer(UInt8)\n\n # ASCII only, and the samples are ASCII. A prelude that folded case for\n # every alphabet would be a Unicode table, which is more lines than this\n # whole file and not what any sample is about.\n def upcase : String\n\n def self.build(capacity : Int32 = 64, & : (String::Builder -> _)) : String\n\n # Interpolation is a call the compiler writes: `\"a#{b}\"` reaches here as\n # `String.interpolation(\"a\", b)` (`LiteralExpander#expand`). The overloads\n # are what let the common shapes avoid work — a single string is itself.\n def self.interpolation(value : String) : String\n\n def self.interpolation(value) : String\n\n def self.interpolation(*values : *T) : String forall T\n\n # Allocates *bytesize* bytes, hands the buffer to the block to fill, and\n # returns the string that results. The character count is left at zero,\n # \"not counted yet\", for `size` to scan the first time it is asked: most\n # strings built here - an encoding, a join, a line - are never asked, and\n # counting them all was a second pass over every byte built.\n #\n # The byte after the last is a NUL, as a literal's is, so `to_unsafe` is\n # a C string: `std/process` and `std/debug` hand one to the platform as a\n # path. The byte was allocated for that and never written, and a string's\n # chunk is atomic - not cleared - so a chunk that had held a longer\n # string kept that string's byte there. Measured on Windows after a\n # collection that freed longer strings: 961 of 1,000 strings built after\n # it had no terminator, and `Process.run` of a path built that way\n # answered \"no such program\" for a file that was there.\n def self.new(bytesize : Int32, & : (Pointer(UInt8) -> Nil)) : String\n\n # A string holding a copy of *count* of *bytes* from *start*, as they\n # are, the range checked: what a module turns bytes it collected into\n # text with, where `String.new(n) { |buffer| ... }` hands it a `Pointer`\n # (SPEC.md III.13).\n def self.new(bytes : Array(UInt8), start : Int32 = 0, count : Int32 = bytes.size - start) : String\nend\n" }, { "name": "Struct", diff --git a/records/wasm/arrays.wasm b/records/wasm/arrays.wasm index d529bc6..8552d66 100755 Binary files a/records/wasm/arrays.wasm and b/records/wasm/arrays.wasm differ diff --git a/records/wasm/blocks.wasm b/records/wasm/blocks.wasm index 4eb056f..1aab61b 100755 Binary files a/records/wasm/blocks.wasm and b/records/wasm/blocks.wasm differ diff --git a/records/wasm/bounds.wasm b/records/wasm/bounds.wasm index 810f8fd..bbe0ced 100755 Binary files a/records/wasm/bounds.wasm and b/records/wasm/bounds.wasm differ diff --git a/records/wasm/case.wasm b/records/wasm/case.wasm index bd2b5c1..12ff1fd 100755 Binary files a/records/wasm/case.wasm and b/records/wasm/case.wasm differ diff --git a/records/wasm/classes.wasm b/records/wasm/classes.wasm index 78cf67e..df5faee 100755 Binary files a/records/wasm/classes.wasm and b/records/wasm/classes.wasm differ diff --git a/records/wasm/conditions.wasm b/records/wasm/conditions.wasm index 5e08ae1..5768c26 100755 Binary files a/records/wasm/conditions.wasm and b/records/wasm/conditions.wasm differ diff --git a/records/wasm/constants.wasm b/records/wasm/constants.wasm index f48e032..796bbaf 100755 Binary files a/records/wasm/constants.wasm and b/records/wasm/constants.wasm differ diff --git a/records/wasm/conversions.wasm b/records/wasm/conversions.wasm index c87bdb5..e489e9b 100755 Binary files a/records/wasm/conversions.wasm and b/records/wasm/conversions.wasm differ diff --git a/records/wasm/defaults.wasm b/records/wasm/defaults.wasm index 52b1fc9..390661d 100755 Binary files a/records/wasm/defaults.wasm and b/records/wasm/defaults.wasm differ diff --git a/records/wasm/defer.wasm b/records/wasm/defer.wasm index d0f65b5..af9172e 100755 Binary files a/records/wasm/defer.wasm and b/records/wasm/defer.wasm differ diff --git a/records/wasm/dispatch.wasm b/records/wasm/dispatch.wasm index e94a843..d8814ea 100755 Binary files a/records/wasm/dispatch.wasm and b/records/wasm/dispatch.wasm differ diff --git a/records/wasm/errors.wasm b/records/wasm/errors.wasm index d029c38..a3de725 100755 Binary files a/records/wasm/errors.wasm and b/records/wasm/errors.wasm differ diff --git a/records/wasm/exports.wasm b/records/wasm/exports.wasm index 78117be..deec3d3 100755 Binary files a/records/wasm/exports.wasm and b/records/wasm/exports.wasm differ diff --git a/records/wasm/functions.wasm b/records/wasm/functions.wasm index a7b4ab3..afa1c55 100755 Binary files a/records/wasm/functions.wasm and b/records/wasm/functions.wasm differ diff --git a/records/wasm/generic-functions.wasm b/records/wasm/generic-functions.wasm index 167e197..2cab967 100755 Binary files a/records/wasm/generic-functions.wasm and b/records/wasm/generic-functions.wasm differ diff --git a/records/wasm/generic-types.wasm b/records/wasm/generic-types.wasm index 84e766f..fed2b0c 100755 Binary files a/records/wasm/generic-types.wasm and b/records/wasm/generic-types.wasm differ diff --git a/records/wasm/hashes.wasm b/records/wasm/hashes.wasm index 22835da..f7bacba 100755 Binary files a/records/wasm/hashes.wasm and b/records/wasm/hashes.wasm differ diff --git a/records/wasm/hello.wasm b/records/wasm/hello.wasm index 54cd580..5773893 100755 Binary files a/records/wasm/hello.wasm and b/records/wasm/hello.wasm differ diff --git a/records/wasm/iyi-basics.wasm b/records/wasm/iyi-basics.wasm index 173c8df..cfae5ef 100755 Binary files a/records/wasm/iyi-basics.wasm and b/records/wasm/iyi-basics.wasm differ diff --git a/records/wasm/iyi-calc.wasm b/records/wasm/iyi-calc.wasm index 7751043..6a24869 100755 Binary files a/records/wasm/iyi-calc.wasm and b/records/wasm/iyi-calc.wasm differ diff --git a/records/wasm/iyi-collections.wasm b/records/wasm/iyi-collections.wasm index b3abadb..f08bbf1 100755 Binary files a/records/wasm/iyi-collections.wasm and b/records/wasm/iyi-collections.wasm differ diff --git a/records/wasm/iyi-config.wasm b/records/wasm/iyi-config.wasm index e72eb5d..2b25fdd 100755 Binary files a/records/wasm/iyi-config.wasm and b/records/wasm/iyi-config.wasm differ diff --git a/records/wasm/iyi-derive.wasm b/records/wasm/iyi-derive.wasm index 68d67e2..29b9d84 100755 Binary files a/records/wasm/iyi-derive.wasm and b/records/wasm/iyi-derive.wasm differ diff --git a/records/wasm/iyi-enums.wasm b/records/wasm/iyi-enums.wasm index a0b018e..0a2eead 100755 Binary files a/records/wasm/iyi-enums.wasm and b/records/wasm/iyi-enums.wasm differ diff --git a/records/wasm/iyi-errors.wasm b/records/wasm/iyi-errors.wasm index 5dba5cb..8836840 100755 Binary files a/records/wasm/iyi-errors.wasm and b/records/wasm/iyi-errors.wasm differ diff --git a/records/wasm/iyi-files.wasm b/records/wasm/iyi-files.wasm index fe94aca..3a80f62 100755 Binary files a/records/wasm/iyi-files.wasm and b/records/wasm/iyi-files.wasm differ diff --git a/records/wasm/iyi-format.wasm b/records/wasm/iyi-format.wasm index 200f399..7408197 100755 Binary files a/records/wasm/iyi-format.wasm and b/records/wasm/iyi-format.wasm differ diff --git a/records/wasm/iyi-formatting.wasm b/records/wasm/iyi-formatting.wasm index 05cb252..bb68754 100755 Binary files a/records/wasm/iyi-formatting.wasm and b/records/wasm/iyi-formatting.wasm differ diff --git a/records/wasm/iyi-generics.wasm b/records/wasm/iyi-generics.wasm index 9eb08cd..9349582 100755 Binary files a/records/wasm/iyi-generics.wasm and b/records/wasm/iyi-generics.wasm differ diff --git a/records/wasm/iyi-grid.wasm b/records/wasm/iyi-grid.wasm index 3de0a78..147c241 100755 Binary files a/records/wasm/iyi-grid.wasm and b/records/wasm/iyi-grid.wasm differ diff --git a/records/wasm/iyi-hello.wasm b/records/wasm/iyi-hello.wasm index ea05c6e..0850333 100755 Binary files a/records/wasm/iyi-hello.wasm and b/records/wasm/iyi-hello.wasm differ diff --git a/records/wasm/iyi-immutable.wasm b/records/wasm/iyi-immutable.wasm index bd09e5f..52643b4 100755 Binary files a/records/wasm/iyi-immutable.wasm and b/records/wasm/iyi-immutable.wasm differ diff --git a/records/wasm/iyi-init_order.wasm b/records/wasm/iyi-init_order.wasm index 7bdc2de..26c5197 100755 Binary files a/records/wasm/iyi-init_order.wasm and b/records/wasm/iyi-init_order.wasm differ diff --git a/records/wasm/iyi-inventory.wasm b/records/wasm/iyi-inventory.wasm index dbef64e..0d5100a 100755 Binary files a/records/wasm/iyi-inventory.wasm and b/records/wasm/iyi-inventory.wasm differ diff --git a/records/wasm/iyi-io.wasm b/records/wasm/iyi-io.wasm index 9cae9e8..dec721b 100755 Binary files a/records/wasm/iyi-io.wasm and b/records/wasm/iyi-io.wasm differ diff --git a/records/wasm/iyi-modules.wasm b/records/wasm/iyi-modules.wasm index 88253a2..1bc5746 100755 Binary files a/records/wasm/iyi-modules.wasm and b/records/wasm/iyi-modules.wasm differ diff --git a/records/wasm/iyi-sessions.wasm b/records/wasm/iyi-sessions.wasm index 11f43ab..ada7016 100755 Binary files a/records/wasm/iyi-sessions.wasm and b/records/wasm/iyi-sessions.wasm differ diff --git a/records/wasm/iyi-shapes.wasm b/records/wasm/iyi-shapes.wasm index aeb71e4..719b982 100755 Binary files a/records/wasm/iyi-shapes.wasm and b/records/wasm/iyi-shapes.wasm differ diff --git a/records/wasm/iyi-socket.wasm b/records/wasm/iyi-socket.wasm index 38d9b99..46f1ce4 100755 Binary files a/records/wasm/iyi-socket.wasm and b/records/wasm/iyi-socket.wasm differ diff --git a/records/wasm/iyi-std_iterator.wasm b/records/wasm/iyi-std_iterator.wasm index 0c74f5c..111789b 100755 Binary files a/records/wasm/iyi-std_iterator.wasm and b/records/wasm/iyi-std_iterator.wasm differ diff --git a/records/wasm/iyi-std_text.wasm b/records/wasm/iyi-std_text.wasm index 358b441..699937a 100755 Binary files a/records/wasm/iyi-std_text.wasm and b/records/wasm/iyi-std_text.wasm differ diff --git a/records/wasm/iyi-std_time.wasm b/records/wasm/iyi-std_time.wasm index 2d03d13..601369c 100755 Binary files a/records/wasm/iyi-std_time.wasm and b/records/wasm/iyi-std_time.wasm differ diff --git a/records/wasm/iyi-visited.wasm b/records/wasm/iyi-visited.wasm index 6841e37..8d596a0 100755 Binary files a/records/wasm/iyi-visited.wasm and b/records/wasm/iyi-visited.wasm differ diff --git a/records/wasm/iyi-webapp.wasm b/records/wasm/iyi-webapp.wasm index b22269f..a555e73 100755 Binary files a/records/wasm/iyi-webapp.wasm and b/records/wasm/iyi-webapp.wasm differ diff --git a/records/wasm/loops.wasm b/records/wasm/loops.wasm index f1bddb6..16db7f9 100755 Binary files a/records/wasm/loops.wasm and b/records/wasm/loops.wasm differ diff --git a/records/wasm/manifest.json b/records/wasm/manifest.json index a7a42c4..c4b04bc 100644 --- a/records/wasm/manifest.json +++ b/records/wasm/manifest.json @@ -1,10 +1,10 @@ { "recorded": { - "compiler": "iyi 0.20.1 (built on Crystal 1.22.0-dev [91d012b1e] (2026-11-04))", - "commit": "91d012b1ed978c7df5cc033624ec161a2daf01b6", + "compiler": "iyi 0.21.0 (built on Crystal 1.22.0-dev [08ce6c1cb] (2026-11-09))", + "commit": "08ce6c1cb2ba5768b15485dbbd6413fcb17df19a", "machine": "Linux 7.0.0-38-generic x86_64, QEMU Virtual CPU version 2.5+", "command": "npm run record:wasm", - "when": "2026-11-04T04:32:07.924Z" + "when": "2026-11-09T17:39:57.770Z" }, "samples": [ { @@ -13,8 +13,8 @@ "path": "samples/tour/hello.iyi", "sourceSha256": "676f8372f2cd5f8941b1dcd0ccb167ee6f6f27c1de4a77cef3af0372c323134d", "wasm": "hello.wasm", - "bytes": 123087, - "sha256": "38d89c17020feacd80582cc0d9b154fa22bd2568d5683b5e4a6294d45bd0ad19", + "bytes": 122956, + "sha256": "ef920fd965f1cc1ec45c4a62206439b0485115cdbb686abcd535ad7a346c801e", "exitCode": 0, "nativeStdout": "Hello Türkiye!\n", "wasmStdout": "Hello Türkiye!\n", @@ -27,8 +27,8 @@ "path": "samples/tour/modules.iyi", "sourceSha256": "c908a80d8c5a2c3f42bc6c90cd9d9a822a4c0b077cbd4713cc627f2d9f4912c0", "wasm": "modules.wasm", - "bytes": 124842, - "sha256": "421a51e54316bf4e162322ab988e25d4e8a18501fcabaf74b2e9a11f61e90e85", + "bytes": 124712, + "sha256": "35c810f0ed42074c9e0ed1623a1a1e9edf5ef02e4325631be59cbb112172ddf7", "exitCode": 0, "nativeStdout": "7\n", "wasmStdout": "7\n", @@ -41,8 +41,8 @@ "path": "samples/tour/exports.iyi", "sourceSha256": "1d341c749781b957ea8e5729a226f84852f816c914224de63771b5a03bfb6153", "wasm": "exports.wasm", - "bytes": 124838, - "sha256": "165e1dc29c8306b68dc489f1be1c4e7cb49d94e2a0dfe37701a60b4ed42a9815", + "bytes": 124708, + "sha256": "9ef16b08e80032a0558a8d12e7983b4535dac75f518ec4e750370d81b2be5639", "exitCode": 0, "nativeStdout": "7\n", "wasmStdout": "7\n", @@ -55,8 +55,8 @@ "path": "samples/tour/variables.iyi", "sourceSha256": "672aec63ec81d0a8dc9cc773688d853a6b9a246618705a4cfa782191efe3fea5", "wasm": "variables.wasm", - "bytes": 156061, - "sha256": "b0a5a9e9e814b7ba37bd613d9c0a7e266a17ebe0a4754bdb4a06ad638dfe78af", + "bytes": 155931, + "sha256": "a494662e1ed8b69ccebb4ca912a7ec862b4bc3aaf94f243c7b9a27f98eb9d294", "exitCode": 0, "nativeStdout": "iyi\n2026\n0.75\ntrue\n2027\n10\n", "wasmStdout": "iyi\n2026\n0.75\ntrue\n2027\n10\n", @@ -69,8 +69,8 @@ "path": "samples/tour/types.iyi", "sourceSha256": "bbd35199bdfae3aa05673d869e512e1713213fa1aeef929e6477fec7ff6e4464", "wasm": "types.wasm", - "bytes": 158793, - "sha256": "f8e6e6e6ffb943a0b51f6f847d1f74d0cd4cfa05d0b285bcb1eb9ee0c65b74c9", + "bytes": 158647, + "sha256": "d8bfb996bfbde179c40161db55f7f2bdedb36771184d5c03037a6a15e168f204", "exitCode": 0, "nativeStdout": "Int32\nInt64\nFloat64\nBool\nChar\nString\n3\n3.5\n1\n1024\n", "wasmStdout": "Int32\nInt64\nFloat64\nBool\nChar\nString\n3\n3.5\n1\n1024\n", @@ -83,8 +83,8 @@ "path": "samples/tour/conversions.iyi", "sourceSha256": "1b8cf5e80ec2dbe3ce9b207d12491e7b24761caf1eb2797a098f68f36e933b81", "wasm": "conversions.wasm", - "bytes": 187416, - "sha256": "ce2964d3ac0e39d59624288868c4e11456da234312570b70a02dd816addca683", + "bytes": 187251, + "sha256": "4fbe6111f086f63e5c38fa2b48ee512889b6927859fe313ceffb02f1c560449f", "exitCode": 0, "nativeStdout": "42.0\n42!\n9\n10.0\n18\n17\n\n65\nB\n", "wasmStdout": "42.0\n42!\n9\n10.0\n18\n17\n\n65\nB\n", @@ -97,8 +97,8 @@ "path": "samples/tour/constants.iyi", "sourceSha256": "e5e15868dc555999be7b1c15e2349147be0e335a5b45792b893aa9c9d3e139ba", "wasm": "constants.wasm", - "bytes": 160443, - "sha256": "7a31ad7c728a18fb7ad4629d223eb6b3f1040b55ed1515d909e6058ee8e82435", + "bytes": 160313, + "sha256": "67a1cf0127db89262934bc336450b946d51aa301629c6ba4a4e2bb1e498a9aa4", "exitCode": 0, "nativeStdout": "12.57\nMerhaba, dünya\ntrue\n", "wasmStdout": "12.57\nMerhaba, dünya\ntrue\n", @@ -111,8 +111,8 @@ "path": "samples/tour/functions.iyi", "sourceSha256": "1261f0c7c47c4f9469358362a6c52a84cd6450a6e4347e1d7ff9eba09a88f89a", "wasm": "functions.wasm", - "bytes": 125184, - "sha256": "a6e3f66932f7ed2580d456041b5553d336b87a0472847092f7dce07d66f592ca", + "bytes": 125021, + "sha256": "41a0a7e59e9e06eb182693b98cacf23110957764a05fd0990a910a77825ee3e2", "exitCode": 0, "nativeStdout": "5\nnegative\nHello, Ayşe!\nSelam, Ayşe!\n", "wasmStdout": "5\nnegative\nHello, Ayşe!\nSelam, Ayşe!\n", @@ -125,8 +125,8 @@ "path": "samples/tour/returns.iyi", "sourceSha256": "6b655fcd036d722550a028424fe4719cdfb89383fed7607e260c46fbf0fff932", "wasm": "returns.wasm", - "bytes": 127402, - "sha256": "6f4b35be779b8d64e92c5d6b95fa959eccd30a9d9574f5d592e16e9cc2132dca", + "bytes": 127223, + "sha256": "055bcac7dff5d27140508b6d2df300e23a0923cbdf01407bbe51e2bdfdfad13a", "exitCode": 0, "nativeStdout": "3\n2\n2\n1\n6\n\n", "wasmStdout": "3\n2\n2\n1\n6\n\n", @@ -139,8 +139,8 @@ "path": "samples/tour/nil.iyi", "sourceSha256": "a559c045cfef442a9927954673d8ad5fc61f610a0908950bcdd79182853a4e04", "wasm": "nil.wasm", - "bytes": 125476, - "sha256": "68356136db87b27634e5c328e02d0386f3e2dd777ce9b463ec1428ab06c25a59", + "bytes": 125313, + "sha256": "fe76a649875f838ba2a12dd16a305952d67b2a6c760a5edada701244aff4f37b", "exitCode": 0, "nativeStdout": "Ada\n\nADA\nnobody\n", "wasmStdout": "Ada\n\nADA\nnobody\n", @@ -153,8 +153,8 @@ "path": "samples/tour/loops.iyi", "sourceSha256": "18f56f89ce4e1bcf3f1c2abff8e3539b5bde85135ec978931583dddd7882992c", "wasm": "loops.wasm", - "bytes": 126424, - "sha256": "e0a0758c488943d7bfe214a973aae9a620649dea031ad74262d68c92865f5173", + "bytes": 126245, + "sha256": "4601a701c8349248119f3959ba6e7b3b857b0425b3af61f3d1e03bfc939a88be", "exitCode": 0, "nativeStdout": "1\n2\n3\nround 0\nround 1\nround 2\n64\ncount 1\ncount 3\ncount 4\n", "wasmStdout": "1\n2\n3\nround 0\nround 1\nround 2\n64\ncount 1\ncount 3\ncount 4\n", @@ -167,8 +167,8 @@ "path": "samples/tour/ranges.iyi", "sourceSha256": "418036dc21744938451d1210eeceaca1931d0edb5329f3f8e801f626fa234fd6", "wasm": "ranges.wasm", - "bytes": 141760, - "sha256": "ce1431275be2f17eb1e7127f660eb3f9466b8639cb67eb14d772ec32da62f1b5", + "bytes": 141565, + "sha256": "83e4c0857a9835996f5b1d5bf3f80addf76bc6c0a0a867d0d2806e620ee11618", "exitCode": 0, "nativeStdout": "1\n2\n3\n4\n5\n1\n2\n3\n4\n[1, 4, 9, 16, 25]\n5050\n[3, 6, 9]\nadult\n", "wasmStdout": "1\n2\n3\n4\n5\n1\n2\n3\n4\n[1, 4, 9, 16, 25]\n5050\n[3, 6, 9]\nadult\n", @@ -181,8 +181,8 @@ "path": "samples/tour/conditions.iyi", "sourceSha256": "8ed0a951d7e760d2bb95c784b20c3e59771080e0578a2cadf0b1bdf41fd2eed2", "wasm": "conditions.wasm", - "bytes": 124828, - "sha256": "0ff133e1140610da7c74ba371bc7385c1763c466778c10a92607d4a2c14e7807", + "bytes": 124697, + "sha256": "11a0bf10a565c4e82f4bfc1f89cba4825342d3a39e84d4da3a9049dd6697a9af", "exitCode": 0, "nativeStdout": "warm\nabove freezing\nnice day\nwarm\nodd\ntrue\nfalse\n", "wasmStdout": "warm\nabove freezing\nnice day\nwarm\nodd\ntrue\nfalse\n", @@ -195,8 +195,8 @@ "path": "samples/tour/case.iyi", "sourceSha256": "71de25c31bbd4ad83f406525df3f263bbb18c8dcab00eee4dba6baa166409ffb", "wasm": "case.wasm", - "bytes": 127806, - "sha256": "eafde9ad418f89622e33a02edfb09460b720f0570fe05d83a5629a3de80b503d", + "bytes": 127643, + "sha256": "edb3cf7bf973662baf5953ba5e682ffec464d0d0f4e3b8437712eda1ffd2aedc", "exitCode": 0, "nativeStdout": "weekend\nalmost\nweekday\nB\na number: 2\n", "wasmStdout": "weekend\nalmost\nweekday\nB\na number: 2\n", @@ -209,8 +209,8 @@ "path": "samples/tour/defer.iyi", "sourceSha256": "c39edacef8695b8aa0170cd169373a19ece2ec4fcf204439ef48e22aadcf06f2", "wasm": "defer.wasm", - "bytes": 128138, - "sha256": "8c66f8e24e1a3fb27ef3b6964f7d15278c7e867b0d84a9fc1817f6e00b1ac523", + "bytes": 127975, + "sha256": "ddee6a7c9f0a955d315cc5e2eebf44243b5a3cde22ffd1e96fb138fc88056386", "exitCode": 0, "nativeStdout": "open\nworking\nclose\n42\nfirst\nsecond\nthird\nturn 0\ndone with 0\nturn 1\ndone with 1\n", "wasmStdout": "open\nworking\nclose\n42\nfirst\nsecond\nthird\nturn 0\ndone with 0\nturn 1\ndone with 1\n", @@ -223,8 +223,8 @@ "path": "samples/tour/structs.iyi", "sourceSha256": "2354525f27753b7a4bdef384dc4c0c6507c12fe845e9d2927e7aec0f62c2814c", "wasm": "structs.wasm", - "bytes": 125572, - "sha256": "653ee06a14e11ed4ba2853ac12e2cf95512e7fa1c534e25d8a37f9d73846c8c1", + "bytes": 125409, + "sha256": "3dda94fa2decc1d27d20a45db6d417717c0b94f2ae75ecf625c6e25f2ba2a820", "exitCode": 0, "nativeStdout": "1\n(4, 6)\n(1, 2)\n", "wasmStdout": "1\n(4, 6)\n(1, 2)\n", @@ -237,8 +237,8 @@ "path": "samples/tour/classes.iyi", "sourceSha256": "cfc6709102991d8da3357f3f6230408b0c79e8bf7199e63f8b40a272c65a2f6a", "wasm": "classes.wasm", - "bytes": 124398, - "sha256": "51e10f413ca4fab84bbe5a3e08f03b0eb24d9b5dbc95a2f867f693f411a866f9", + "bytes": 124267, + "sha256": "88a0313b6c78c1fdf00ebcb18d0ec1ea56a34a0292ee5a4286d88e10254948ad", "exitCode": 0, "nativeStdout": "2\n0\n", "wasmStdout": "2\n0\n", @@ -251,8 +251,8 @@ "path": "samples/tour/arrays.iyi", "sourceSha256": "7f4841af80023b70c3e6f897ec7d5ba82001acd91a905e4772cc6f6f0dec0aa9", "wasm": "arrays.wasm", - "bytes": 145881, - "sha256": "b6f59b198213a93a2aafcdd3c786e0a8d0d57c1ecd140163d3fc4ced243d2272", + "bytes": 145688, + "sha256": "8dcb6f4c0ad2b9ab87c5bc976698bbecb9b378000286615da9c441312e8d83c2", "exitCode": 0, "nativeStdout": "5\n3\n3\n5\n[3, 1, 4, 1, 5, 9]\ntrue\n23\n[1, 1, 3, 4, 5, 9]\n[3, 4, 5, 9]\n[30, 10, 40, 10, 50, 90]\n23\nAyşe, Mehmet\n1. Ayşe\n2. Mehmet\n", "wasmStdout": "5\n3\n3\n5\n[3, 1, 4, 1, 5, 9]\ntrue\n23\n[1, 1, 3, 4, 5, 9]\n[3, 4, 5, 9]\n[30, 10, 40, 10, 50, 90]\n23\nAyşe, Mehmet\n1. Ayşe\n2. Mehmet\n", @@ -265,8 +265,8 @@ "path": "samples/tour/hashes.iyi", "sourceSha256": "1b5fb76d25d7f5979248800422d871da36d4c2d0b829ec9c311c7b200beb167b", "wasm": "hashes.wasm", - "bytes": 165179, - "sha256": "edf321c2fe8ad10a22a36bd2d1693c8e3a68023acd16c9c436877b596acb12ee", + "bytes": 164991, + "sha256": "176c7902f92e229650183a2b9276096d8a94e68eaf402fe70fc3d1890f5f7888", "exitCode": 0, "nativeStdout": "31\n2\n[\"Ayşe\", \"Mehmet\", \"Zeynep\"]\n\nfalse\nAyşe is 31\nMehmet is 27\nZeynep is 40\n{\"a\" => 2, \"b\" => 1}\n", "wasmStdout": "31\n2\n[\"Ayşe\", \"Mehmet\", \"Zeynep\"]\n\nfalse\nAyşe is 31\nMehmet is 27\nZeynep is 40\n{\"a\" => 2, \"b\" => 1}\n", @@ -279,8 +279,8 @@ "path": "samples/tour/tuples.iyi", "sourceSha256": "75f2414a08f54a9d8ecc53d7b0621d8c4906a3f39d9a673dd1b8d2f219b4856b", "wasm": "tuples.wasm", - "bytes": 127759, - "sha256": "6056cbfa88e63e5d611ac94fa18b401da1f24eaffbe01f1a4ddb644d73cf43f3", + "bytes": 127580, + "sha256": "c2379513cf5ca45885251791e09dbcedfb7566613f816a4a33a1da1c63d5b6df", "exitCode": 0, "nativeStdout": "1\none\n1 is one\nAyşe\n31\n2..9\n", "wasmStdout": "1\none\n1 is one\nAyşe\n31\n2..9\n", @@ -293,8 +293,8 @@ "path": "samples/tour/blocks.iyi", "sourceSha256": "f2ae3fc23344a9f94f896942b8609956979599387e313e98d49cb4977ba9dd17", "wasm": "blocks.wasm", - "bytes": 127866, - "sha256": "e01d093cb97f30a1facc40fba0d44e3dd6898d0656431943b26f953fc7efa4fd", + "bytes": 127703, + "sha256": "af559636ccc1150e88aeefcfd273139e64560d977cd409d1d394decb65fae22b", "exitCode": 0, "nativeStdout": "step 0\nstep 1\nstep 2\n6\n49\n2\n105\n", "wasmStdout": "step 0\nstep 1\nstep 2\n6\n49\n2\n105\n", @@ -307,8 +307,8 @@ "path": "samples/tour/methods.iyi", "sourceSha256": "f4fb9b8be821d24f79148d110edb8f6bd36239bab164d7be45bc4d3da29eba7a", "wasm": "methods.wasm", - "bytes": 161051, - "sha256": "3d85a2d848d5011a19920b4762269a04aa7bac800a563275d46ff1264cb055bd", + "bytes": 160906, + "sha256": "821ecee2fe4e090a47bdbb893fe155856deede021eefbbde1d7a47304784c8a0", "exitCode": 0, "nativeStdout": "12.0\nfalse\n48.0\nIYI\n4.0\n6\n", "wasmStdout": "12.0\nfalse\n48.0\nIYI\n4.0\n6\n", @@ -321,8 +321,8 @@ "path": "samples/tour/traits.iyi", "sourceSha256": "98aaeb457da3a65d50c3ce5402f3378e431010d50122396f9a353b4d958fc8a8", "wasm": "traits.wasm", - "bytes": 160535, - "sha256": "1dccca26c8177bf515393a4db94b2f7fd31507fe822479a1d9770f0315ad9497", + "bytes": 160405, + "sha256": "83fd8dd9c69b9ac870f02af15fddd21908bd5d4ac1016ede5260235b2391e169", "exitCode": 0, "nativeStdout": "3.14\n4.0\n", "wasmStdout": "3.14\n4.0\n", @@ -335,8 +335,8 @@ "path": "samples/tour/dispatch.iyi", "sourceSha256": "e9a5985790ff7c52c42846b394872bb18ac9040abfd59e9453d4ac71747fcfb9", "wasm": "dispatch.wasm", - "bytes": 136069, - "sha256": "00325eb2fcd4ec3a1af9143c6ff51df4bd1ac9c171a583b5d1b8ccb0ec9ff530", + "bytes": 135890, + "sha256": "14ec898f10936049a2728dd6d26146f59eb1d41bf85d4888dfe99a4ae29d88a9", "exitCode": 0, "nativeStdout": "hav\nmiyav\nhav miyav\n", "wasmStdout": "hav\nmiyav\nhav miyav\n", @@ -349,8 +349,8 @@ "path": "samples/tour/defaults.iyi", "sourceSha256": "7e438eff3837000a1551861b7107a2a4f7f6f78988a283fec3174429a1f20844", "wasm": "defaults.wasm", - "bytes": 127100, - "sha256": "11fd75705a649460a5e68bcc738382a79b12cb33783069f7748d6aca563d3513", + "bytes": 126953, + "sha256": "5a8dff82b106a9e221d3a481ef20310fafe57dc8d3a7c20dbf72942e556bde9f", "exitCode": 0, "nativeStdout": "Merhaba, Ada!\nMERHABA, ADA!\nBEEP unit 7\nBEEP UNIT 7\n", "wasmStdout": "Merhaba, Ada!\nMERHABA, ADA!\nBEEP unit 7\nBEEP UNIT 7\n", @@ -363,8 +363,8 @@ "path": "samples/tour/unions.iyi", "sourceSha256": "61cb534c8687c14b7392657f859b165eb063dc892ae64429ccc59dd241441466", "wasm": "unions.wasm", - "bytes": 135190, - "sha256": "1b1d08ebe15d3c2adec4e4efd232cb05f7fa75c3c9cdfd3b6b9362ff973e4627", + "bytes": 135017, + "sha256": "99383b5a7eb8432ec23d4115aa8bb96183b29027c64cc354e537f377a4402d83", "exitCode": 0, "nativeStdout": "number 24\nproblem, not a number: x\nnumber 14\n6\n", "wasmStdout": "number 24\nproblem, not a number: x\nnumber 14\n6\n", @@ -377,8 +377,8 @@ "path": "samples/tour/errors.iyi", "sourceSha256": "af4c28f720136cd6c8b935273fa769fe234b55716da4abed70196e56d8b9faf7", "wasm": "errors.wasm", - "bytes": 160371, - "sha256": "28f5753b1b2208e441777f7725c79c5edabcf1e2fca60a5bee643785aa926dd8", + "bytes": 160221, + "sha256": "e424a6f3a64e545cbcb717d3df919d36f5b4f8f45f5c53ccd2101968facdac48", "exitCode": 0, "nativeStdout": "2027\nno such key: missing\ndefault\niyi\n", "wasmStdout": "2027\nno such key: missing\ndefault\niyi\n", @@ -391,8 +391,8 @@ "path": "samples/tour/strings.iyi", "sourceSha256": "704f192ecd578a9847d911563129b559b5b04cc276327f8de24fd1502565f542", "wasm": "strings.wasm", - "bytes": 160621, - "sha256": "c48fff3f2df3aa6fd46e301aa0944db7dd8b51b5255cfccfcb08cc63125e875d", + "bytes": 160417, + "sha256": "d7523ad329c72f4a8513fd83ea7304acf5a54a23eb20b47755c6a665c1a7f678", "exitCode": 0, "nativeStdout": "10\nHELLO, IYI\nhello, iyi\ntrue\nHello, iyi!\n[\"Hello,\", \"iyi\"]\n2 and true and [1, 2]\n12,05 TL\ntotal: 12,05 TL\nitem 12\ntotal 99\n", "wasmStdout": "10\nHELLO, IYI\nhello, iyi\ntrue\nHello, iyi!\n[\"Hello,\", \"iyi\"]\n2 and true and [1, 2]\n12,05 TL\ntotal: 12,05 TL\nitem 12\ntotal 99\n", @@ -405,8 +405,8 @@ "path": "samples/tour/generic-functions.iyi", "sourceSha256": "2b0ace59365d9bb35c904102d1131a95f5787113ba585975a819c6a354666e7f", "wasm": "generic-functions.wasm", - "bytes": 151699, - "sha256": "858cbbd01e1e6f1a5b490b2f4b83c20cbc02649b8c5618eda024760c5f846ca2", + "bytes": 151511, + "sha256": "f111d27e4cf2575ce8cd778c4fad28469b2b308cb50e4c0f390dba5f8fe290e3", "exitCode": 0, "nativeStdout": "10\na\n{\"one\", 1}\n101\n", "wasmStdout": "10\na\n{\"one\", 1}\n101\n", @@ -419,8 +419,8 @@ "path": "samples/tour/generic-types.iyi", "sourceSha256": "fe5f0b5626ef062f7af44196125d3b50d1914788fb1416d980d10e22f872aa8f", "wasm": "generic-types.wasm", - "bytes": 135597, - "sha256": "0a11fc1ef4fbbd9c159cee034d0086ee874a84e6a4f19e83ed62e1c95c185f9b", + "bytes": 135418, + "sha256": "0563fbcd284ece245c11193b322cb433a6ff1472cc9a102009624d6698e04f3e", "exitCode": 0, "nativeStdout": "2\n2\n1\n\niyi\na stack of 1\na stack of 0\n", "wasmStdout": "2\n2\n1\n\niyi\na stack of 1\na stack of 0\n", @@ -433,8 +433,8 @@ "path": "samples/tour/bounds.iyi", "sourceSha256": "cd5324bcc2756eef8d4ebeca406c61ecf12cfd5898b26a01ec8fa4fd1ef64d26", "wasm": "bounds.wasm", - "bytes": 129541, - "sha256": "073f84a1a4f1aa838ad40a6df38051c8f30fce51553a3f31a977d2d976457853", + "bytes": 129394, + "sha256": "8a8597464a0625ba034b5efea731c14bdb156d67f1ee8dbf510bd187b69008c7", "exitCode": 0, "nativeStdout": "200\n45\n30\n", "wasmStdout": "200\n45\n30\n", @@ -447,8 +447,8 @@ "path": "samples/iyi/basics.iyi", "sourceSha256": "2fab386bd172c15319db0a4e553008d7ae7114fd8cdef72e7a48a850c301c72c", "wasm": "iyi-basics.wasm", - "bytes": 213443, - "sha256": "024d922379b0d19585337915d3847c2d8ab5b689f63a090f458cc09e8e23ff16", + "bytes": 213228, + "sha256": "178d7d42bc89b5ef3be94701dc1d7c84a7acafb7038d8e04e36c1d33185a3282", "exitCode": 0, "nativeStdout": "55\n1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz\n3\n4,5,9,6\ntrue\n1,1,2,3,4,5,6,9\n3,1,4,1,5,9,2,6\n1,1,2,3,4,5,6,9\n31\n31\nhello, world\ntrue\n12\nKhoor, Zruog\n3\n3\n{\"a\" => 3, \"b\" => 1, \"c\" => 1}\n14\nthe 5\ncat 2\nsat 2\n[\"cat\", \"end\"]\n", "wasmStdout": "55\n1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz\n3\n4,5,9,6\ntrue\n1,1,2,3,4,5,6,9\n3,1,4,1,5,9,2,6\n1,1,2,3,4,5,6,9\n31\n31\nhello, world\ntrue\n12\nKhoor, Zruog\n3\n3\n{\"a\" => 3, \"b\" => 1, \"c\" => 1}\n14\nthe 5\ncat 2\nsat 2\n[\"cat\", \"end\"]\n", @@ -459,10 +459,10 @@ "id": "iyi/calc", "set": "iyi", "path": "samples/iyi/calc.iyi", - "sourceSha256": "9844743a83b1d7bd80eceef04339216ef04e8a446c800f9c50da265442f8dc8e", + "sourceSha256": "2b2b272b47b884c709a008f99c3770c131084159e1d4c9f6514b586d76f8bffa", "wasm": "iyi-calc.wasm", - "bytes": 200618, - "sha256": "9eba1a8c39a31abf400ba374a183b7cb44cf4f502409883caba1d175943e99b0", + "bytes": 200374, + "sha256": "1f7b0ff15241f08c81554cb896711c36728939fe74e2497cc0aa3b6b124d8fd6", "exitCode": 0, "nativeStdout": "64\n", "wasmStdout": "64\n", @@ -475,8 +475,8 @@ "path": "samples/iyi/collections.iyi", "sourceSha256": "9b8bb4c0bf703238a08afe44995207bb1cb6292578a7d255b5f3ece0c2a80213", "wasm": "iyi-collections.wasm", - "bytes": 228588, - "sha256": "c27bcfda04e61f7b14dee7b8f846e2a323865e72df156a646f1b400819af1f27", + "bytes": 228254, + "sha256": "c0f9fc75f16de2fd74fa2a1b586cfc8bf4eecdfe764d03e1e947c2b7a457032b", "exitCode": 0, "nativeStdout": "to_a 3,1,4,1,5\nsize 5\nempty? false true\nall? true\nany? true\nnone? true\none? true\ncount 2\nfind 4 nil\nindex 2 nil\nfirst?/first 3 3\nmap 6,2,8,2,10\nflat_map 3,3,1,1,4,4,1,1,5,5\nmap_with_index 0,1,8,3,20\nselect 3,4,5\nreject 1,1\npartition 3+4+5 | 1+1\ntake/skip 3,1 1,5\ntake_while 3,1\nskip_while 4,1,5\nreduce 14\neach_with_index 0,1,2,3,4\neach_with_object 30,10,40,10,50\njoin 3-1-4-1-5\nincludes? true false\nmax?/max 5 5\nmin?/min 1 1\nsum/product 14 60\nsum of nothing 0\ntally 2\nsorted 1,1,3,4,5\nmax_by pear\ngroup_by pear,plum,kiwi\nindex_by fig\neach_slice 3+1 4+1 5\neach_cons 3+1 1+4 4+1 1+5\nchunk_while 3 1+4 1+5\nfirst(n) 3,1,4\ncompact_map 30,40,50\nfind_value big\naccumulate 0,3,4,8,9,14\nreduce? 14\nin_groups_of 3+1 4+1 5+0\nmin_by fig\nmax_of/min_of 4 3\nminmax 1,5\ntally_by 3\nsum_by 15\nzip 3pear 1fig 4plum 1kiwi\ncycle 1,2,1,2\nas a type 5 items / 4 items\n", "wasmStdout": "to_a 3,1,4,1,5\nsize 5\nempty? false true\nall? true\nany? true\nnone? true\none? true\ncount 2\nfind 4 nil\nindex 2 nil\nfirst?/first 3 3\nmap 6,2,8,2,10\nflat_map 3,3,1,1,4,4,1,1,5,5\nmap_with_index 0,1,8,3,20\nselect 3,4,5\nreject 1,1\npartition 3+4+5 | 1+1\ntake/skip 3,1 1,5\ntake_while 3,1\nskip_while 4,1,5\nreduce 14\neach_with_index 0,1,2,3,4\neach_with_object 30,10,40,10,50\njoin 3-1-4-1-5\nincludes? true false\nmax?/max 5 5\nmin?/min 1 1\nsum/product 14 60\nsum of nothing 0\ntally 2\nsorted 1,1,3,4,5\nmax_by pear\ngroup_by pear,plum,kiwi\nindex_by fig\neach_slice 3+1 4+1 5\neach_cons 3+1 1+4 4+1 1+5\nchunk_while 3 1+4 1+5\nfirst(n) 3,1,4\ncompact_map 30,40,50\nfind_value big\naccumulate 0,3,4,8,9,14\nreduce? 14\nin_groups_of 3+1 4+1 5+0\nmin_by fig\nmax_of/min_of 4 3\nminmax 1,5\ntally_by 3\nsum_by 15\nzip 3pear 1fig 4plum 1kiwi\ncycle 1,2,1,2\nas a type 5 items / 4 items\n", @@ -489,8 +489,8 @@ "path": "samples/iyi/config.iyi", "sourceSha256": "e1c60e92bb4150097bed4d7986dc678380776ef09fc95ff03b278746ff121c10", "wasm": "iyi-config.wasm", - "bytes": 177376, - "sha256": "a64c12afa8154579a053691b75b5aab9c38be7423a69ebf6e72669b29edb56e3", + "bytes": 177188, + "sha256": "1bfd5ef6fa4138bbad00c1b4aee56be2ea3bb1973f23cf02120e3a2f61fd287d", "exitCode": 0, "nativeStdout": "{\"name\" => \"iyi\", \"port\" => \"8080\", \"mode\" => \"fast\"}\n8081\nname, port, mode\nerror: line 2: no '=' in \"broken line\"\n[\"a\", \"\", \"b\"]\n[\"one\", \"two\"]\n\"padded\"\ntrue\n", "wasmStdout": "{\"name\" => \"iyi\", \"port\" => \"8080\", \"mode\" => \"fast\"}\n8081\nname, port, mode\nerror: line 2: no '=' in \"broken line\"\n[\"a\", \"\", \"b\"]\n[\"one\", \"two\"]\n\"padded\"\ntrue\n", @@ -503,8 +503,8 @@ "path": "samples/iyi/derive.iyi", "sourceSha256": "ab33030ac91db9cddf89e68f39e93fe96296f028f313e6e3c25afa76851aa32a", "wasm": "iyi-derive.wasm", - "bytes": 128268, - "sha256": "b5e0d05aaaa29c0218472ee8d118c58325694f2f9fb1d62c44bd54a106e180bf", + "bytes": 128075, + "sha256": "5ee7d4c53ccfd539385c501ee1cee9b79b95085c0cb6cc369cd6f56a83de7c6b", "exitCode": 0, "nativeStdout": "Point(@x=3 @y=4 )\nSpan(@from=1 @to=9 @label=range )\nTag(@label=Std::Derives::Name @count=2 )\n@label= \n", "wasmStdout": "Point(@x=3 @y=4 )\nSpan(@from=1 @to=9 @label=range )\nTag(@label=Std::Derives::Name @count=2 )\n@label= \n", @@ -517,8 +517,8 @@ "path": "samples/iyi/enums.iyi", "sourceSha256": "98b275c766e680a4adc21a3fc80c18b94e954317dd4668a4b303fcbeea8c0c5d", "wasm": "iyi-enums.wasm", - "bytes": 151146, - "sha256": "0c4a1549d7642c41f006cb74806180daf689263e36015a665e3060c1406bcb0f", + "bytes": 150944, + "sha256": "ff547c733f8e2b2c2c2c48f37781d146388f94a5c37b22c0be63418f1dc63030", "exitCode": 0, "nativeStdout": "level: Warn\nspelled: Error\nsame: true\nother: false\nverdict: loud\nwarn? true\nerror? false\nat least info: true\nbelow error: true\nDebug Info Warn Error \nnames: 4\nparsed: Error\nlenient: Warn\nunknown: nil\nvalue: 3\nfrom 1: Info\nfrom 9: nil\nmode: Read | Write\nread: true\nappend: false\nnone: None\nmasked: Write\nwarns: 2\nerrors: 1\n", "wasmStdout": "level: Warn\nspelled: Error\nsame: true\nother: false\nverdict: loud\nwarn? true\nerror? false\nat least info: true\nbelow error: true\nDebug Info Warn Error \nnames: 4\nparsed: Error\nlenient: Warn\nunknown: nil\nvalue: 3\nfrom 1: Info\nfrom 9: nil\nmode: Read | Write\nread: true\nappend: false\nnone: None\nmasked: Write\nwarns: 2\nerrors: 1\n", @@ -531,8 +531,8 @@ "path": "samples/iyi/errors.iyi", "sourceSha256": "89e937ddfc973d79b5f00c8ea3b81315a68b5c2c6cf4d1bac4172a3da1b79bd0", "wasm": "iyi-errors.wasm", - "bytes": 138685, - "sha256": "edf9cb5dd070d1099e51ea5297f54cb1260135ed6874c553029c63593777412d", + "bytes": 138398, + "sha256": "7fa1eb40ac78e8281b5e0c1bf6659568209e6f15786653b5efaed9324094d4a2", "exitCode": 0, "nativeStdout": "read 12\nio: no such file: missing\nparse: not a number: twelve\nor: 12\nor: 0\nor: 0\nor: -1\npanic: 12\nsuccess:\n open outer\n open inner\n close inner\n close outer\n12\npropagating:\n open outer\n close outer\n-1\n iteration 0\n iteration 0 closed\n iteration 1\n iteration 1 closed\n", "wasmStdout": "read 12\nio: no such file: missing\nparse: not a number: twelve\nor: 12\nor: 0\nor: 0\nor: -1\npanic: 12\nsuccess:\n open outer\n open inner\n close inner\n close outer\n12\npropagating:\n open outer\n close outer\n-1\n iteration 0\n iteration 0 closed\n iteration 1\n iteration 1 closed\n", @@ -545,8 +545,8 @@ "path": "samples/iyi/files.iyi", "sourceSha256": "aaa45bbe135b26d2c882122da7a4fe69df4bcba517a6f8ed57ba0101eda8ba69", "wasm": "iyi-files.wasm", - "bytes": 125156, - "sha256": "938f2c61da810eafcf644405d304bfbb860920f9929fe22c1fa8ca2a6a0655bf", + "bytes": 124986, + "sha256": "dc2b5364d3b2a091fb9f55e8635d6330bdf0e815e26ec54c30547d8010eb9f73", "exitCode": 1, "nativeStdout": "hello from iyi\ntrue\nfalse\nfalse\n", "wasmStdout": "", @@ -559,8 +559,8 @@ "path": "samples/iyi/format.iyi", "sourceSha256": "54f6992a74d0bff957fed60b05e8bd9d7bb16b091ea1b79e8277acd7454128bb", "wasm": "iyi-format.wasm", - "bytes": 308041, - "sha256": "d9342a14e3491780072b1038e479579ef3724e056e12fe7f56fccf1eb9080622", + "bytes": 307790, + "sha256": "127ecc49920e01c282eda220de3f44113e91c0a7d62edeefe359a70b0589fdd2", "exitCode": 0, "nativeStdout": "hello world\nchar 'A' (ascii 65)\nright ' hi'\nleft 'hi '\ndec: 255\nhex: ff\nHEX: 0XFF\noct: 0o377\nbin: 0b101\nplus: +42\nzero: 000042\nsign zero: +00042\nnegative zero: -00042\nint precision: 0007\nfloat precision: 3.14\nfloat precision: 3.1416\nstring precision: cin\nwider than field: 12345\nzero precision float: 4\nzero precision zero int: ' '\nmin int64: -9223372036854775808\nmax int64: 9223372036854775807\nString.format: answer = 42\npercent tuple: score = 0099\nprintf: run completed\n", "wasmStdout": "hello world\nchar 'A' (ascii 65)\nright ' hi'\nleft 'hi '\ndec: 255\nhex: ff\nHEX: 0XFF\noct: 0o377\nbin: 0b101\nplus: +42\nzero: 000042\nsign zero: +00042\nnegative zero: -00042\nint precision: 0007\nfloat precision: 3.14\nfloat precision: 3.1416\nstring precision: cin\nwider than field: 12345\nzero precision float: 4\nzero precision zero int: ' '\nmin int64: -9223372036854775808\nmax int64: 9223372036854775807\nString.format: answer = 42\npercent tuple: score = 0099\nprintf: run completed\n", @@ -573,8 +573,8 @@ "path": "samples/iyi/formatting.iyi", "sourceSha256": "ad90bbbd17da587770609673949d55c990aee356dda1d437ce23394231ec75ee", "wasm": "iyi-formatting.wasm", - "bytes": 143143, - "sha256": "455c2338487d1440594743329e00b74f1ea682bf18e35aac8a526ad4144e9bfd", + "bytes": 142929, + "sha256": "120bf5c2346e4e8653053f6fd893bdbe4a53cd112eb1c6a260b10a1f777d40fa", "exitCode": 0, "nativeStdout": "last 30\nfirst 10\nwritten 99\nchar o\nslice lo\nhex ff\nbin 101\nrjust ' ab'\nljust 'ab***'\nrep ababab\nnil nil\nmaybe 99\n", "wasmStdout": "last 30\nfirst 10\nwritten 99\nchar o\nslice lo\nhex ff\nbin 101\nrjust ' ab'\nljust 'ab***'\nrep ababab\nnil nil\nmaybe 99\n", @@ -587,8 +587,8 @@ "path": "samples/iyi/generics.iyi", "sourceSha256": "b3ec6ebe033571c8bedecdb4d00a5df97eeca44b709d94352066c8f215f4bbe1", "wasm": "iyi-generics.wasm", - "bytes": 143824, - "sha256": "6a8727c576c6679e07701cfd869abd86fee9b5c054dfd59b1a5f054b472d1923", + "bytes": 143623, + "sha256": "a1b5fd209438d6b5203facdf1367f96c44ea41eb75f0ab2b97d78dbc57dd9050", "exitCode": 0, "nativeStdout": "Box(42)\nBox(hello)\nfirst=1, second=two\nBox(1)\nBox(two)\nfirst=3, second=four\nada, ada\n84\ntrue\n7\n", "wasmStdout": "Box(42)\nBox(hello)\nfirst=1, second=two\nBox(1)\nBox(two)\nfirst=3, second=four\nada, ada\n84\ntrue\n7\n", @@ -601,8 +601,8 @@ "path": "samples/iyi/grid.iyi", "sourceSha256": "2f28278d0111d0ecc5c45afa5c2de9841abb5ad5f0041417dc97caa4fbc5338f", "wasm": "iyi-grid.wasm", - "bytes": 187747, - "sha256": "fc5548ba269a5eb3ea6ad618af6fcf49f263afe863eb1225e63e50b71f66cf69", + "bytes": 187554, + "sha256": "318342b3d01409aab03666c0f5dfffc19c649038db0fc0b4090f9c2fce9f497a", "exitCode": 0, "nativeStdout": " 0 1 2 3\n 4 5 6 7\n 8 9 10 11\n 12 13 14 15\n120\n[0, 5, 10, 15]\n[0, 4, 8, 12]\n[5, 4, 3, 2, 1]\n[2, 4, 6, 8, 10]\n55\n[3, 1, 2]\n[{1, \"a\"}, {2, \"b\"}, {3, \"c\"}]\n2\na\n[\"b\", \"c\"]\n01234\n", "wasmStdout": " 0 1 2 3\n 4 5 6 7\n 8 9 10 11\n 12 13 14 15\n120\n[0, 5, 10, 15]\n[0, 4, 8, 12]\n[5, 4, 3, 2, 1]\n[2, 4, 6, 8, 10]\n55\n[3, 1, 2]\n[{1, \"a\"}, {2, \"b\"}, {3, \"c\"}]\n2\na\n[\"b\", \"c\"]\n01234\n", @@ -615,8 +615,8 @@ "path": "samples/iyi/hello.iyi", "sourceSha256": "27cdcc173bc626bf84c26f468628026979b15bc6127e67594c5adf2930483a83", "wasm": "iyi-hello.wasm", - "bytes": 130696, - "sha256": "bfed0df44969235bb0212ac02af2c40bd3e58ecb9588c82fcd8ee03b0d9e5bf1", + "bytes": 130494, + "sha256": "be61bb7662c9332837aeb96a60712f324d9a934dd5b6ce6968a788d4de07366d", "exitCode": 0, "nativeStdout": "Hello, iyi!\nHELLO, IYI!\nBEEP 42\nHello, crystal!\nBEEP 7\n-> BEEP 9\n", "wasmStdout": "Hello, iyi!\nHELLO, IYI!\nBEEP 42\nHello, crystal!\nBEEP 7\n-> BEEP 9\n", @@ -629,8 +629,8 @@ "path": "samples/iyi/immutable.iyi", "sourceSha256": "ffdb52f45c0426a876e109c9e26862d43d4bbc7bca547612e8b31dc5a10e7b9a", "wasm": "iyi-immutable.wasm", - "bytes": 197267, - "sha256": "11d4ebd1d2dcce4bad066884d950579d96256ecdb018ed985c61ceab8258e7d6", + "bytes": 196966, + "sha256": "8507aa9733887b221c4c720acaadda51f0a8b7bb2855d9647e0d7abd7a7fcc47", "exitCode": 0, "nativeStdout": "size 5\nempty? false true\nat 3 5\nappended 6 from 5, original 3,1,4,1,5\nconcatenated 3,1,4,1,5,7 / 5\ncopied list 2, source 3\nto_a 3,1,4,1,5\nall?/any? true true\nnone?/one? true true\ncount 2\nfind 4 nil\nindex 2 nil\nfirst?/first 3 3\nmap 6,2,8,2,10\nflat_map 3,3,1,1,4,4,1,1,5,5\nmap_with_index 0,1,8,3,20\nselect/reject 3,4,5 1,1\npartition 3+4+5 | 1+1\ntake/skip 3,1 1,5\ntake_while 3,1\nskip_while 4,1,5\nreduce 14\neach_with_object 30,10,40,10,50\njoin 3-1-4-1-5\nincludes? true false\nmax/min 5 1\nsum/product 14 60\ntally 2\nsorted 1,1,3,4,5\nmax_by pear\ngroup_by pear,plum,kiwi\ntwo elem types 4,2,5,2,6 / 4,3,4,4\n", "wasmStdout": "size 5\nempty? false true\nat 3 5\nappended 6 from 5, original 3,1,4,1,5\nconcatenated 3,1,4,1,5,7 / 5\ncopied list 2, source 3\nto_a 3,1,4,1,5\nall?/any? true true\nnone?/one? true true\ncount 2\nfind 4 nil\nindex 2 nil\nfirst?/first 3 3\nmap 6,2,8,2,10\nflat_map 3,3,1,1,4,4,1,1,5,5\nmap_with_index 0,1,8,3,20\nselect/reject 3,4,5 1,1\npartition 3+4+5 | 1+1\ntake/skip 3,1 1,5\ntake_while 3,1\nskip_while 4,1,5\nreduce 14\neach_with_object 30,10,40,10,50\njoin 3-1-4-1-5\nincludes? true false\nmax/min 5 1\nsum/product 14 60\ntally 2\nsorted 1,1,3,4,5\nmax_by pear\ngroup_by pear,plum,kiwi\ntwo elem types 4,2,5,2,6 / 4,3,4,4\n", @@ -643,8 +643,8 @@ "path": "samples/iyi/init_order.iyi", "sourceSha256": "0eee830465aeffb6ce0cabe6230e1c8dd87b37458bb42f9ba9b567721dcdc340", "wasm": "iyi-init_order.wasm", - "bytes": 124809, - "sha256": "b0f607519ec0dd50324fcb841e32ede39fe8f203908d6476de67ee438bf76416", + "bytes": 124600, + "sha256": "c1cc05d3928ba5606954b0b27bb381f5f8f43e262d6e42988d510e0f35fc34e8", "exitCode": 0, "nativeStdout": "1. boot/config\n2. boot/registry, above its own `import` line\n3. boot/registry, below it\n4. samples/init_order, above its own `import` lines\n5. Hello, iyi.\n", "wasmStdout": "1. boot/config\n2. boot/registry, above its own `import` line\n3. boot/registry, below it\n4. samples/init_order, above its own `import` lines\n5. Hello, iyi.\n", @@ -657,8 +657,8 @@ "path": "samples/iyi/inventory.iyi", "sourceSha256": "8d17a1ab3590cbe103b0eedb8487c9df8cc2837e0b84b26a3c8abbe353af80d6", "wasm": "iyi-inventory.wasm", - "bytes": 218027, - "sha256": "5b3186682657003e9a8ab9fda0c3d04b4b6e563f1a35e16d0576681edd83a52e", + "bytes": 217819, + "sha256": "0a3a5f5d7027d2202c58a736546c20464f30edaa19a91225bb98afb48886062a", "exitCode": 0, "nativeStdout": "total: 11.0\ncheap: apple, pear\n2\ntrue\ntrue\nmelon\npear\n3.0\nnil\n1. apple 10 5.0\n2. pear 4 3.0\n3. melon 1 3.0\n[\"apple\", \"pear\", \"melon\"]\n15\npear\n", "wasmStdout": "total: 11.0\ncheap: apple, pear\n2\ntrue\ntrue\nmelon\npear\n3.0\nnil\n1. apple 10 5.0\n2. pear 4 3.0\n3. melon 1 3.0\n[\"apple\", \"pear\", \"melon\"]\n15\npear\n", @@ -671,8 +671,8 @@ "path": "samples/iyi/io.iyi", "sourceSha256": "8878852be923dc681f060baf0ae863e04a30e87f123498813c37738c2c97d41f", "wasm": "iyi-io.wasm", - "bytes": 131609, - "sha256": "6b01baf6530675f1c0de0ffa3b59514e636ae74e6612b50d93ec7b82d1755cbd", + "bytes": 131423, + "sha256": "addfb8a4cd3ecefa8b07f6acc71e342e7294401c1340326c24f0737941275647", "exitCode": 0, "nativeStdout": "no lines read from standard input\n", "wasmStdout": "no lines read from standard input\n", @@ -685,8 +685,8 @@ "path": "samples/iyi/modules.iyi", "sourceSha256": "f4ea9f6e29e18cf9452eccc75ba8324c25d108f825b64765f44335c22c57e0be", "wasm": "iyi-modules.wasm", - "bytes": 126751, - "sha256": "aefa7c4a462776d1d49bb86f7b8d17a2c6eee4855a055e064a3f17aebade4bf6", + "bytes": 126558, + "sha256": "37608851b57a2f39bf41ea69a986ea0e118111a7ee2bb3e557937b797f680144", "exitCode": 0, "nativeStdout": "HELLO, IYI. Good day, iyi.\nfriend\nesteemed colleague\n", "wasmStdout": "HELLO, IYI. Good day, iyi.\nfriend\nesteemed colleague\n", @@ -699,8 +699,8 @@ "path": "samples/iyi/sessions.iyi", "sourceSha256": "d33fc3528bffdd710b785425e5b0559e606c53184c2234774d56fc7d9f2f55c9", "wasm": "iyi-sessions.wasm", - "bytes": 139540, - "sha256": "4485718365ed0640af3e3bddbbc19174f9524a49bff26f6619a3cfb07ca326c9", + "bytes": 139354, + "sha256": "217e57575528b77efbac5d06b076748d9ec6ee416961431412afca04b6a9b9e2", "exitCode": 0, "nativeStdout": "three arrivals:\n ada since tick 1\n linus since tick 2\n grace since tick 3\n (3 signed in)\nlinus left, had been in since tick 2\n ada since tick 1\n grace since tick 3\n (2 signed in)\nnobody named bob: true\nlinus is back:\n ada since tick 1\n grace since tick 3\n linus since tick 4\n (3 signed in)\n ada since tick 1\n grace since tick 3\n linus since tick 4\n (3 signed in)\n", "wasmStdout": "three arrivals:\n ada since tick 1\n linus since tick 2\n grace since tick 3\n (3 signed in)\nlinus left, had been in since tick 2\n ada since tick 1\n grace since tick 3\n (2 signed in)\nnobody named bob: true\nlinus is back:\n ada since tick 1\n grace since tick 3\n linus since tick 4\n (3 signed in)\n ada since tick 1\n grace since tick 3\n linus since tick 4\n (3 signed in)\n", @@ -713,8 +713,8 @@ "path": "samples/iyi/shapes.iyi", "sourceSha256": "e3e4ca261b7f764ce2a168163c6da93909efc63d663fb1828f7b6d5a79b9f805", "wasm": "iyi-shapes.wasm", - "bytes": 197147, - "sha256": "1cd492ea85bbe9e4a9564b00c2737250ab7bea9fd339735e1484882e3d6ace71", + "bytes": 196953, + "sha256": "d0d572cc4b983896a286828771c62889d539ff5c517049d4934ee54f6a32244d", "exitCode": 0, "nativeStdout": "circle: 3.14\nrect: 6.0\n9.1\nrect\n[12, nil, 7, -3]\n16\n[12, 7, 3]\n1024\n0.25\n1099511627776\n128\n8\n", "wasmStdout": "circle: 3.14\nrect: 6.0\n9.1\nrect\n[12, nil, 7, -3]\n16\n[12, 7, 3]\n1024\n0.25\n1099511627776\n128\n8\n", @@ -727,8 +727,8 @@ "path": "samples/iyi/socket.iyi", "sourceSha256": "afb4e7913d7b5c01a6e3ba5f42b3945e6ede3a374552325d07fa51ac25fa43e3", "wasm": "iyi-socket.wasm", - "bytes": 413767, - "sha256": "6f04c78fd4d8e50b90c8d359d7421fc35a208207b664e2d20726e2278935dfd1", + "bytes": 413416, + "sha256": "e31277b6cc159544185c93e8b917fced32756bab0f8d3b81700d31d9b210f757", "exitCode": 1, "nativeStdout": "server listening on an ephemeral port\nserver received: hello from client\nclient received: hello from server\nclosed\n", "wasmStdout": "", @@ -741,8 +741,8 @@ "path": "samples/iyi/std_iterator.iyi", "sourceSha256": "fdfedfc80d758e3490725c1fc6ebaa5648f5066854a28ccf1eb8cdb90b872fc8", "wasm": "iyi-std_iterator.wasm", - "bytes": 208688, - "sha256": "4b03bfefec927743879d96208e917c1b76c74ed48789a16f29bb33c3bdbcb049", + "bytes": 208409, + "sha256": "bf302c50800db30c3c0f4790ae36e17a6e52aaec4a8dcd2178e0761a4ef2b046", "exitCode": 0, "nativeStdout": "natural numbers first 5: [1, 2, 3, 4, 5]\npulls from generator: 5\neven squares first 4: [4, 16, 36, 64]\npulls for 4 matches: 8\nchained odds and evens: [1, 3, 5, 2, 4, 6]\nzipped pairs: [{\"apple\", 100}, {\"banana\", 95}, {\"cherry\", 88}]\neach_slice(3): [[1, 2, 3], [4, 5, 6], [7]]\neach_cons(3): [[10, 20, 30], [20, 30, 40], [30, 40, 50]]\nstepped by 2: [0, 20, 40, 60]\ncycled 2 times: [\"tick\", \"tock\", \"tick\", \"tock\"]\nwith_index(1): [{\"alpha\", 1}, {\"beta\", 2}, {\"gamma\", 3}]\nreduce sum: 100\nfirst > 10: 12\n", "wasmStdout": "natural numbers first 5: [1, 2, 3, 4, 5]\npulls from generator: 5\neven squares first 4: [4, 16, 36, 64]\npulls for 4 matches: 8\nchained odds and evens: [1, 3, 5, 2, 4, 6]\nzipped pairs: [{\"apple\", 100}, {\"banana\", 95}, {\"cherry\", 88}]\neach_slice(3): [[1, 2, 3], [4, 5, 6], [7]]\neach_cons(3): [[10, 20, 30], [20, 30, 40], [30, 40, 50]]\nstepped by 2: [0, 20, 40, 60]\ncycled 2 times: [\"tick\", \"tock\", \"tick\", \"tock\"]\nwith_index(1): [{\"alpha\", 1}, {\"beta\", 2}, {\"gamma\", 3}]\nreduce sum: 100\nfirst > 10: 12\n", @@ -755,8 +755,8 @@ "path": "samples/iyi/std_text.iyi", "sourceSha256": "c891f86fe35a876e7f8279c549a0d16a39f38715593fae69a4ca387cb6468276", "wasm": "iyi-std_text.wasm", - "bytes": 195805, - "sha256": "58b65f1dc5af2b855f2cbafa2d65b28915a0664a28b44b69109f783e4ff3858d", + "bytes": 195601, + "sha256": "06554edb04647247f4ac9a4faf6a8b466ff36868e22f52e3c9dac930c664f93d", "exitCode": 0, "nativeStdout": "cleaned line: 'GET /api/v1/users HTTP/1.1'\nHTTP method: GET, path: /api/v1/users, version: HTTP/1.1\nHeader 'Authorization' = 'Bearer token123'\nslug: hello-world-this-is-iyi-std-text-\ngreeting size: 11 characters, 12 bytes\nreversed greeting: dlrow olléh\nhex 'ff' to decimal: 255\n", "wasmStdout": "cleaned line: 'GET /api/v1/users HTTP/1.1'\nHTTP method: GET, path: /api/v1/users, version: HTTP/1.1\nHeader 'Authorization' = 'Bearer token123'\nslug: hello-world-this-is-iyi-std-text-\ngreeting size: 11 characters, 12 bytes\nreversed greeting: dlrow olléh\nhex 'ff' to decimal: 255\n", @@ -769,8 +769,8 @@ "path": "samples/iyi/std_time.iyi", "sourceSha256": "82a17d942f25fe20e124340ce9245293d280594fc4e07ee5a4e82dee62dd3620", "wasm": "iyi-std_time.wasm", - "bytes": 275376, - "sha256": "d494d5133d6a7daee036be9fe3610dc070da42ca509794c468f848959b6dcf7e", + "bytes": 275128, + "sha256": "0e942606afe8574d341ef6499907ce530ab8608c915bab5810019811f7335149", "exitCode": 0, "nativeStdout": "Reference time (UTC): 2024-02-29 12:30:45 UTC\nRFC 3339: 2024-02-29T12:30:45.123Z\nUnix timestamp: 1709209845 s (1709209845123 ms)\nYear: 2024\nMonth: 2\nDay: 29\nDay of week: Thursday\nDay of year: 60\nTomorrow: 2024-03-01T12:30:45Z\nYesterday: 2024-02-28T12:30:45Z\nLive wall clock valid: true\nMonotonic duration >= 0: true\nLeap day event: 2024-02-29 12:00:00 UTC\nIs leap year: true\n", "wasmStdout": "Reference time (UTC): 2024-02-29 12:30:45 UTC\nRFC 3339: 2024-02-29T12:30:45.123Z\nUnix timestamp: 1709209845 s (1709209845123 ms)\nYear: 2024\nMonth: 2\nDay: 29\nDay of week: Thursday\nDay of year: 60\nTomorrow: 2024-03-01T12:30:45Z\nYesterday: 2024-02-28T12:30:45Z\nLive wall clock valid: true\nMonotonic duration >= 0: true\nLeap day event: 2024-02-29 12:00:00 UTC\nIs leap year: true\n", @@ -783,8 +783,8 @@ "path": "samples/iyi/visited.iyi", "sourceSha256": "987608a702df760c9cb5f3c0c85a5f621553fda885504892acb2fbb67d57d744", "wasm": "iyi-visited.wasm", - "bytes": 176116, - "sha256": "ed4b948df48e36d0f8dd7b4d73e31e9b937ee1aaae31a282027a047b49336e35", + "bytes": 175905, + "sha256": "0f55455c765aab9f74309960a5d887ff285880d2f6d1c741727f83d13085230e", "exitCode": 0, "nativeStdout": "Set{\"home\", \"docs\", \"blog\", \"install\", \"about\"}\n5\nfalse\nSet{\"install\"}\nSet{\"orphan\"}\ntrue\nfalse\ntrue\ntrue\n[\"the\", \"cat\", \"and\", \"dog\", \"bird\"]\n", "wasmStdout": "Set{\"home\", \"docs\", \"blog\", \"install\", \"about\"}\n5\nfalse\nSet{\"install\"}\nSet{\"orphan\"}\ntrue\nfalse\ntrue\ntrue\n[\"the\", \"cat\", \"and\", \"dog\", \"bird\"]\n", @@ -797,8 +797,8 @@ "path": "samples/iyi/webapp.iyi", "sourceSha256": "5bba5cdefa6407b2fe87e8c84bfdcf195bf02177193fbaea3e646c143018f296", "wasm": "iyi-webapp.wasm", - "bytes": 171353, - "sha256": "c8989407961055e536ceb461549618dcd6fa29740e2635bfeb43c956bc08ddc2", + "bytes": 170915, + "sha256": "72baa89a90057462ae41cf3baecfcbaa02ad05246622132de16fac0d16853a46", "exitCode": 0, "nativeStdout": "GET /\nGET /users\nPOST /users\nGET /count\nGET /v1\nGET /v1/admin/dashboard\n1|42|0\n", "wasmStdout": "GET /\nGET /users\nPOST /users\nGET /count\nGET /v1\nGET /v1/admin/dashboard\n1|42|0\n", @@ -831,7 +831,7 @@ "id": "iyi/cores", "set": "iyi", "path": "samples/iyi/cores.iyi", - "sourceSha256": "0f5c2670ab8af0eb23c403e053464e75c0fa2e4d053d556ac8c52331d526bc14", + "sourceSha256": "d7dbe787e38d1821182ba3aca3390c90e00fe1a1858299cff027bb620e924b3a", "refusal": "Error: std/system_roots reads the operating system's trusted roots, and this target has none: Linux, darwin and Windows do.", "reason": "A server on every core has no wasm32-wasi build to run. The program imports std/http, whose TLS reads the operating system's trusted roots through std/system_roots, and that module refuses to compile for a target with none; and its tasks, like every task, do not compile on wasm32 yet (SPEC.md III.4.8).", "exitCode": 0, diff --git a/records/wasm/methods.wasm b/records/wasm/methods.wasm index 29a1df8..61cbfa3 100755 Binary files a/records/wasm/methods.wasm and b/records/wasm/methods.wasm differ diff --git a/records/wasm/modules.wasm b/records/wasm/modules.wasm index 0c3aa49..d66c022 100755 Binary files a/records/wasm/modules.wasm and b/records/wasm/modules.wasm differ diff --git a/records/wasm/nil.wasm b/records/wasm/nil.wasm index 3251665..a9abdd6 100755 Binary files a/records/wasm/nil.wasm and b/records/wasm/nil.wasm differ diff --git a/records/wasm/ranges.wasm b/records/wasm/ranges.wasm index 56f19cf..cbcaa27 100755 Binary files a/records/wasm/ranges.wasm and b/records/wasm/ranges.wasm differ diff --git a/records/wasm/returns.wasm b/records/wasm/returns.wasm index 0a9f90e..d906a48 100755 Binary files a/records/wasm/returns.wasm and b/records/wasm/returns.wasm differ diff --git a/records/wasm/strings.wasm b/records/wasm/strings.wasm index 3c52403..89a09ff 100755 Binary files a/records/wasm/strings.wasm and b/records/wasm/strings.wasm differ diff --git a/records/wasm/structs.wasm b/records/wasm/structs.wasm index 60b8ca7..4cfe4e9 100755 Binary files a/records/wasm/structs.wasm and b/records/wasm/structs.wasm differ diff --git a/records/wasm/traits.wasm b/records/wasm/traits.wasm index 5fead95..7ff9cbb 100755 Binary files a/records/wasm/traits.wasm and b/records/wasm/traits.wasm differ diff --git a/records/wasm/tuples.wasm b/records/wasm/tuples.wasm index ec0ae85..d96051d 100755 Binary files a/records/wasm/tuples.wasm and b/records/wasm/tuples.wasm differ diff --git a/records/wasm/types.wasm b/records/wasm/types.wasm index 97f6631..a5aa3c6 100755 Binary files a/records/wasm/types.wasm and b/records/wasm/types.wasm differ diff --git a/records/wasm/unions.wasm b/records/wasm/unions.wasm index f130dec..08d6ac7 100755 Binary files a/records/wasm/unions.wasm and b/records/wasm/unions.wasm differ diff --git a/records/wasm/variables.wasm b/records/wasm/variables.wasm index a2cb4a7..eeee9b4 100755 Binary files a/records/wasm/variables.wasm and b/records/wasm/variables.wasm differ diff --git a/scripts/budget.json b/scripts/budget.json index 0a0b0ac..1976fd8 100644 --- a/scripts/budget.json +++ b/scripts/budget.json @@ -34,6 +34,8 @@ { "match": "/install/", "ceiling": 229376 }, { "match": "/learn/", "ceiling": 245760 }, { "match": "/learn/*/", "ceiling": 245760 }, + { "match": "/blog/", "ceiling": 212992 }, + { "match": "/blog/*/", "ceiling": 229376 }, { "match": "/playground/", "ceiling": 229376 }, { "match": "/playground/iyi/*/", "ceiling": 262144 }, { "match": "/playground/evidence/", "ceiling": 327680 }, diff --git a/scripts/notes.mjs b/scripts/notes.mjs new file mode 100755 index 0000000..f9b403d --- /dev/null +++ b/scripts/notes.mjs @@ -0,0 +1,158 @@ +#!/usr/bin/env node +// The release notes a blog post quotes, read out of CHANGELOG.md at the +// release's own tag. +// +// WHY AT THE TAG. Every other page on this site is regenerated against the +// current release, and that is right for a page that describes the language +// as it is. A post is the opposite: it is dated, and it says what one release +// changed. Quoting the working tree's CHANGELOG.md would let a later edit to +// that release's entry move a sentence inside a post that was published +// before the edit, and quoting `facts.json` would turn the figures in an old +// announcement into this week's. A tag does not move, so a post that quotes +// its release through this file says the same thing for as long as it is up. +// +// WHICH RELEASES. The ones the posts name. Each post under src/content/blog +// that carries `release:` in its frontmatter asks for that release's section, +// and nothing else is read; a post that names a release with no tag fails +// here, by name, because the post would otherwise be quoting a changelog +// nobody published. +// +// WHAT IT WRITES. Per release: its tag, the commit the tag names, the date +// its heading states, and the section cut into blocks - a paragraph, a list +// item or a `###` heading - each with the lines it spans in the file at that +// tag. `src/components/ReleaseNote.astro` finds a block by a substring and +// prints it with those lines, so the citation under a quotation is the +// generator's and not the author's. + +import { mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { spawnSync } from "node:child_process"; +import { blobAt } from "./release-ref.mjs"; + +const here = dirname(fileURLToPath(import.meta.url)); +const site = resolve(here, ".."); +const repo = process.env.IYI_REPO ? resolve(process.env.IYI_REPO) : resolve(site, "..", "iyi"); +const posts = resolve(site, "src", "content", "blog"); +const out = resolve(site, "src", "generated"); + +const SOURCE = "CHANGELOG.md"; + +/* The heading shape scripts/releases.mjs and scripts/release-ref.mjs read. */ +const HEADING = /^##\s+(\d+\.\d+\.\d+)\s+[—-]+\s+(\d{4}-\d{2}-\d{2})\s*$/; + +/* `release:` in a post's frontmatter, quoted or not. The collection schema in + * src/content.config.ts holds the field's shape; this only has to find it. */ +const RELEASE = /^release:\s*["']?(\d+\.\d+\.\d+)["']?\s*$/m; + +const asked = new Map(); +for (const name of readdirSync(posts).filter((n) => /\.mdx?$/.test(n)).sort()) { + const text = readFileSync(resolve(posts, name), "utf8"); + const match = RELEASE.exec(/^---\n([\s\S]*?)\n---/.exec(text)?.[1] ?? ""); + if (!match) continue; + const list = asked.get(match[1]) ?? []; + list.push(`src/content/blog/${name}`); + asked.set(match[1], list); +} + +/** The release's section of CHANGELOG.md at its tag, cut into blocks. */ +function section(version, askedBy) { + const tag = `v${version}`; + const commit = spawnSync("git", ["-C", repo, "rev-parse", "--verify", `${tag}^{commit}`], { + encoding: "utf8", + }); + if (commit.status !== 0) { + throw new Error( + `notes: ${askedBy.join(", ")} quotes ${version} and ${repo} has no ${tag}. ` + + `Fetch the tags ("git -C ${repo} fetch --tags"), or the post names a ` + + `release that was never published.`, + ); + } + + const bytes = blobAt(repo, tag, SOURCE); + if (bytes === null) { + throw new Error(`notes: ${tag} has no ${SOURCE}, so ${askedBy.join(", ")} has nothing to quote.`); + } + const lines = bytes.toString("utf8").split("\n"); + + const start = lines.findIndex((line) => HEADING.exec(line)?.[1] === version); + if (start === -1) { + throw new Error( + `notes: ${SOURCE} at ${tag} has no heading for ${version} that reads as ` + + `"## ${version} — date", so the section ${askedBy.join(", ")} quotes ` + + `cannot be found. The heading shape moved.`, + ); + } + const date = HEADING.exec(lines[start])[2]; + const next = lines.findIndex((line, i) => i > start && /^## /.test(line)); + const end = next === -1 ? lines.length : next; + + /* A block is a run of non-blank lines, except that a list item's own + * continuation paragraphs, indented under it after a blank line, stay with + * it: the item is one entry in the changelog and is quoted as one. */ + const blocks = []; + let current = null; + const close = () => { + if (current === null) return; + blocks.push(current); + current = null; + }; + for (let i = start + 1; i < end; i++) { + const line = lines[i]; + const number = i + 1; + if (line.trim() === "") { + if (current?.kind === "item") { + const following = lines.slice(i + 1, end).find((l) => l.trim() !== ""); + if (following !== undefined && /^\s{2,}\S/.test(following)) { + current.paragraphs.push([]); + continue; + } + } + close(); + continue; + } + const heading = /^###\s+(.*)$/.exec(line); + const item = /^- (.*)$/.exec(line); + if (heading) { + close(); + blocks.push({ kind: "heading", from: number, to: number, paragraphs: [[heading[1]]] }); + } else if (item) { + close(); + current = { kind: "item", from: number, to: number, paragraphs: [[item[1]]] }; + } else { + current ??= { kind: "paragraph", from: number, to: number, paragraphs: [[]] }; + current.paragraphs.at(-1).push(line.trim()); + current.to = number; + } + } + close(); + + return { + version, + date, + tag, + commit: commit.stdout.trim(), + source: SOURCE, + blocks: blocks.map(({ kind, from, to, paragraphs }) => ({ + kind, + from, + to, + paragraphs: paragraphs.filter((p) => p.length > 0).map((p) => p.join(" ")), + })), + }; +} + +const releases = {}; +for (const [version, askedBy] of asked) releases[version] = section(version, askedBy); + +mkdirSync(out, { recursive: true }); +writeFileSync( + resolve(out, "notes.json"), + `${JSON.stringify({ provenance: { generator: "scripts/notes.mjs", source: SOURCE }, releases }, null, 2)}\n`, + "utf8", +); + +const summary = Object.values(releases) + .map((r) => `${r.version} (${r.blocks.length} blocks at ${r.tag})`) + .join(", "); +console.log(`notes: ${summary || "no post quotes a release"}`); diff --git a/src/components/H2.astro b/src/components/H2.astro new file mode 100644 index 0000000..268f719 --- /dev/null +++ b/src/components/H2.astro @@ -0,0 +1,8 @@ +--- +/** The `##` of a lesson or a post, mapped onto the anchored heading beside this file. */ +import Heading from "./Heading.astro"; + +const { id } = Astro.props; +--- + + diff --git a/src/components/H3.astro b/src/components/H3.astro new file mode 100644 index 0000000..4e10cc2 --- /dev/null +++ b/src/components/H3.astro @@ -0,0 +1,8 @@ +--- +/** The `###` of a lesson or a post, mapped onto the anchored heading beside this file. */ +import Heading from "./Heading.astro"; + +const { id } = Astro.props; +--- + + diff --git a/src/pages/learn/_Heading.astro b/src/components/Heading.astro similarity index 77% rename from src/pages/learn/_Heading.astro rename to src/components/Heading.astro index 7976632..c6e348b 100644 --- a/src/pages/learn/_Heading.astro +++ b/src/components/Heading.astro @@ -1,18 +1,18 @@ --- /** - * A heading inside a lesson's prose, with a link to itself beside it. + * A heading inside a lesson's or a post's prose, with a link to itself beside + * it. * * The id is not invented here. Astro's markdown pipeline slugs every heading - * already, which is what the in-page contents list in `[...slug].astro` links - * to, and MDX hands the rendered element's attributes to whatever component is - * mapped over it. So this reads the id it was given and refuses to link when - * there is none, rather than deriving a second slug that could disagree with - * the first and send a reader to an anchor that is not on the page. + * already, which is what a lesson's in-page contents list links to, and MDX + * hands the rendered element's attributes to whatever component is mapped over + * it. So this reads the id it was given and refuses to link when there is + * none, rather than deriving a second slug that could disagree with the first + * and send a reader to an anchor that is not on the page. * - * Underscore prefixed so Astro does not route it: a component that lives beside - * the page it serves, which is the only page that can use it, because MDX - * component mappings are handed to `` by the page doing the - * rendering. + * It is mapped by the pages that render ``, src/pages/learn/[...slug].astro + * and src/pages/blog/[slug].astro, because MDX component mappings are handed + * to `` by the page doing the rendering. * * The two wrappers beside this file exist because an MDX mapping is keyed on * the element name and carries no depth with it, so the level has to be written diff --git a/src/components/ReleaseNote.astro b/src/components/ReleaseNote.astro new file mode 100644 index 0000000..79a6d7b --- /dev/null +++ b/src/components/ReleaseNote.astro @@ -0,0 +1,61 @@ +--- +/** + * A block of a release's notes, quoted in a post. + * + * The text is CHANGELOG.md's at the release's tag, read by scripts/notes.mjs + * and found here by a substring through src/lib/posts.ts, so a post cannot + * paraphrase its release and call it a quotation, and the line range under + * the block is the generator's. Set in the sans, because the notes are prose a + * person wrote; the citation is in the mono, because a file and its lines are + * the machine's. + */ +import { inlineMarkdown } from "../lib/lessons"; +import { releaseNote } from "../lib/posts"; + +interface Props { + release: string; + /** A substring of the one block to quote. */ + find: string; +} + +const { release, find } = Astro.props; +const note = releaseNote(release, find); +const lines = note.from === note.to ? `line ${note.from}` : `lines ${note.from} to ${note.to}`; +--- + + + + diff --git a/src/content.config.ts b/src/content.config.ts index 1ab91c4..833ea16 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -115,4 +115,39 @@ const learn = defineCollection({ }), }); -export const collections = { learn }; +/** + * The posts: release announcements, and whatever else the project has to say + * on a date. + * + * A post is the one page on this site that is about a moment rather than about + * the tree as it is, so its shape says which moment. `date` is when it was + * published and does not move. `release`, where a post announces one, names + * the release, and it is checked twice: src/lib/posts.ts refuses a release + * CHANGELOG.md does not state, or a post dated before it, and scripts/notes.mjs + * reads that release's section out of CHANGELOG.md at its tag, which is what a + * post quotes through `ReleaseNote`. So the quotations in an announcement are + * the release's own words and stay its words when the next release comes out. + * + * No figure is typed into a post, for the reason none is typed anywhere here: + * scripts/no-transcription.mjs reads src/content. The figures a release + * measured are in its notes, with the machines they were measured on. + */ +const blog = defineCollection({ + loader: glob({ base: "./src/content/blog", pattern: "**/*.{md,mdx}" }), + schema: z.object({ + title: z.string().min(1), + // What a results page prints under the title, and what the index and the + // feed say of the post. Plain text, for the reason a lesson's is. + description: z + .string() + .max(155) + .regex(/^[^`]*$/, "description is plain text: no backticks"), + date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "date is the day it was published, as `2026-11-09`"), + release: z + .string() + .regex(/^\d+\.\d+\.\d+$/, "release is a version CHANGELOG.md states, such as `0.21.0`") + .optional(), + }), +}); + +export const collections = { learn, blog }; diff --git a/src/content/blog/iyi-0-21-0.mdx b/src/content/blog/iyi-0-21-0.mdx new file mode 100644 index 0000000..6b9f3d6 --- /dev/null +++ b/src/content/blog/iyi-0-21-0.mdx @@ -0,0 +1,119 @@ +--- +title: "iyi 0.21.0: one spawn verb, parallel where it is safe" +description: "iyi 0.21.0 gives spawn one verb, runs a plain spawn in parallel where the compiler proves it safe, and makes a module that handles raw memory say so." +date: "2026-11-09" +release: "0.21.0" +--- + +import ReleaseNote from "../../components/ReleaseNote.astro"; +import Sample from "../../components/Sample.astro"; +import Quote from "../../components/Quote.astro"; + +iyi 0.21.0 is out. It is a release about tasks: how a program starts one, where +it runs, and how a test finds the bug that only one order of them shows. It is +also the release in which a module that reaches memory the compiler does not +check has to say so. Here is the release in its own words: + + + +## One verb for a task + +There used to be three ways to start a task and two keyword arguments to +qualify them. Now there is `g.spawn`, and an optional `unit:` that says where +the task runs. A *unit* is a set of tasks of which at most one runs at a time: +the main thread with its tasks, a task started with `unit: :new` with the tasks +it starts, or a `Unit` value. + +- `g.spawn { }` lets the compiler place the task. +- `g.spawn(unit: :new) { }` asks for a unit of its own, on another thread, and + holds the block to everything `spawn_parallel` was held to: it may capture + only what is `Share`. +- `g.spawn(unit: :same) { }` keeps the task in its spawner's unit whatever it + captures. + +The cores sample in the release shows both sides. The first group appends to an +`Array` it captured, which is not `Share`, so its plain spawns stay on their +spawner's thread and take turns; the second asks for units of their own and +hands each answer back once the task ends. + + + +## Parallel where the compiler can prove it + +A plain `spawn` is no longer always serial. When its block passes every check +`unit: :new` would apply, and nothing it reaches through its calls is module +state that is not `Share` (other than state marked `@[SafeReach]`, which the +compiler now proves rather than trusts), it runs on the pool in a unit of its +own. Any other spawn stays where it was. Nothing that compiled stops compiling. + +You are not left to guess which way a spawn went. `iyi check` notes each spawn +it kept in its spawner's unit, with the capture or the write that kept it +there; `--explain-spawn` on `build`, `check` and `run` writes the verdict for +every spawn; and the language server shows the same verdict as an inlay hint +after the `spawn`. + +For state that only one unit should touch, `std/unit` adds `Unit(T)`: a value +whose tasks, started with `g.spawn(in: u)`, never run at the same instant as +each other, the way an actor's do. + +## Data parallelism, and tests that shake the schedule + +`std/parallel` cuts a `List`, an `Array` or an integer range into chunks across +the workers: `each_parallel`, `map_parallel`, `sum_parallel` and +`reduce_parallel`. `map_parallel` answers what `map` does, in order, and a +failure or a panic in a chunk reaches the caller. + +The compiler rules out memory races; ordering races are what is left. `iyi test +--chaos` runs each test once per seed, and every scheduling choice is drawn from +the seed: which task runs next, where one is preempted, which ready `select` arm +goes first. A test that passes only because its tasks happened to run in one +order fails, and prints the line that replays the same schedule: +`iyi test --chaos=SEED file`. + +## Raw memory says so + +Until this release any `.iyi` file could reach memory the compiler does not +check without ever spelling `Pointer`, through `to_unsafe` and a buffer. Now a +module whose first statement is `@[Unsafe("why")]` may do that, and every other +module is refused at its first such operation, with the mark offered as the +edit. The reason is required. + +Most modules that only read or build bytes do not need the mark at all: +`String#byte_at`, `Bytes.of` and `String.new(Array(UInt8))` do it without raw +memory. `iyi fix` inserts the mark, the language server offers it as a quick +fix, and `iyi migrate` writes it into a module whose Crystal source handles raw +memory. + +## Underneath + +The runtime got a lot of work that a program does not have to ask for. Each +channel has a lock of its own. A `std/mutex` lock and its release take two +locked instructions, and a waiter that starves is handed the lock. The worker +pool starts one worker and grows to the work. A collection has a ceiling, and a +size class the program stopped allocating in gives its memory back. A build +compiles its units on threads of their own, and a `--release` build optimises +on every core. + +Smaller things: `iyi init` with no module path names the project after its +directory, a path typed in another case on a Mac is the file it names, and two +pointers compare equal when they hold the same address. + +## What you have to change + + + +In practice, run `iyi fix` over the project. The old spawn spellings are compile +errors that name the new one and carry it as an edit, so `iyi fix` rewrites them +where the call has a block, and it inserts `@[Unsafe("why")]` where a module +needs it, with a reason for you to replace with the real one. Where a module only +read or built bytes, moving it to `byte_at` and `Bytes.of` is the better fix. +Libraries need releases that carry their own marks, and iyi-web, iyi-pg, iyi-db +and jwt have them. + +## Upgrade + +The installer fetches the latest release: + + + + diff --git a/src/layouts/Page.astro b/src/layouts/Page.astro index 622a619..2ee6a0c 100644 --- a/src/layouts/Page.astro +++ b/src/layouts/Page.astro @@ -64,7 +64,7 @@ interface Props { title: string; summary?: string; /** Schema.org type of the page's main content; WebPage unless a page says. */ - kind?: "WebPage" | "TechArticle" | "CollectionPage"; + kind?: "WebPage" | "TechArticle" | "CollectionPage" | "Blog" | "BlogPosting"; /** * Whether this page is a URL a crawler should keep. * @@ -85,9 +85,14 @@ interface Props { * in deciding the page's `dateModified`. */ source?: string; + /** + * The day a post was published, from its frontmatter, written into the + * structured data as `datePublished`. + */ + published?: string; } -const { title, summary, kind = "WebPage", indexable = true, source } = Astro.props; +const { title, summary, kind = "WebPage", indexable = true, source, published } = Astro.props; const base = import.meta.env.BASE_URL; const href = (p: string) => `${base.replace(/\/$/, "")}/${p.replace(/^\//, "")}`; const here = Astro.url.pathname; @@ -142,7 +147,9 @@ const ogImage = new URL(href("og.png"), Astro.site).href; * is found from the route's pattern, `/learn/[...slug]` being * src/pages/learn/[...slug].astro; a pattern that names no file is a broken * mapping here, not a page without a date, so it fails the build. A file with - * no commit yet has no date of its own and the release's stands. */ + * no commit yet has no date of its own and the release's stands. A post goes + * by the same rule: the listings and commands it sets follow the release like + * any page's. Its own day is `datePublished`, below. */ const routeFile = (() => { const pattern = Astro.routePattern.replace(/\/$/, ""); const candidates = [".astro", ".ts", "/index.astro"].map((ext) => `src/pages${pattern}${ext}`); @@ -171,6 +178,7 @@ const sections: [string, string][] = [ ["Reference", "reference/"], ["Playground", "playground/"], ["Project", "project/"], + ["Blog", "blog/"], ]; const trail = indexable && here !== href("") @@ -252,6 +260,10 @@ const ld = { ...(indexable ? { url: canonical } : {}), name: fullTitle, description: summary, + /* A post's headline is its own title, without the site's name the + * results-page title carries, and it was published on a day. */ + ...(kind === "BlogPosting" ? { headline: title, author: { "@id": organization } } : {}), + ...(published ? { datePublished: published } : {}), dateModified, isPartOf: { "@id": `${Astro.site}#website` }, about: { "@id": `${Astro.site}#iyi` }, @@ -282,6 +294,7 @@ const nav = [ ["Reference", "reference/"], ["Agents", "agents/"], ["Playground", "playground/"], + ["Blog", "blog/"], ["Install", "install/"], ]; @@ -307,8 +320,9 @@ const current = (path: string) => here.startsWith(href(path)); + - + {indexable && } diff --git a/src/lib/posts.ts b/src/lib/posts.ts new file mode 100644 index 0000000..fd0a3cf --- /dev/null +++ b/src/lib/posts.ts @@ -0,0 +1,114 @@ +import { getCollection, type CollectionEntry } from "astro:content"; +import notes from "../generated/notes.json"; +import install from "../generated/install-targets.json"; +import { releases, type Release } from "./release"; + +export type Post = CollectionEntry<"blog">; + +/** + * Every post, newest first, with what its frontmatter claims checked. + * + * A post that names a release names one CHANGELOG.md states, and is dated on + * or after that release's own date: an announcement written before the + * release it announces is a draft, and a draft is not published. Two posts on + * one URL cannot happen (the file name is the slug), but two on one day can, + * so the order breaks a tie by slug rather than by whatever order the loader + * found the files in. + */ +export async function posts(): Promise { + const stated = new Map(releases().map((release) => [release.version, release])); + const all = await getCollection("blog"); + for (const post of all) { + const version = post.data.release; + if (version === undefined) continue; + const release = stated.get(version); + if (release === undefined) { + throw new Error( + `posts: ${post.filePath} announces ${version}, and CHANGELOG.md states no ` + + `such release. Either the iyi checkout is not at the release, or the ` + + `post names one that was never published.`, + ); + } + if (post.data.date < release.date) { + throw new Error( + `posts: ${post.filePath} is dated ${post.data.date}, before ${version} ` + + `was released on ${release.date}.`, + ); + } + } + return all.sort((a, b) => + a.data.date === b.data.date ? a.id.localeCompare(b.id) : b.data.date.localeCompare(a.data.date), + ); +} + +/** The release a post announces, as CHANGELOG.md states it. */ +export function releaseOf(post: Post): Release | undefined { + const version = post.data.release; + return version === undefined ? undefined : releases().find((r) => r.version === version); +} + +/** The post that announces a release, if one does. */ +export async function announcementOf(release: Release): Promise { + return (await posts()).find((post) => post.data.release === release.version); +} + +/** Where a post lives, with the deployment's base honoured. */ +export function postHref(base: string, post: Post): string { + return `${base.replace(/\/$/, "")}/blog/${post.id}/`; +} + +/** A date as a reader reads it: `9 November 2026`. */ +export function longDate(date: string): string { + return new Date(`${date}T00:00:00Z`).toLocaleDateString("en-GB", { + day: "numeric", + month: "long", + year: "numeric", + timeZone: "UTC", + }); +} + +type Block = { kind: string; from: number; to: number; paragraphs: string[] }; +type Section = { + version: string; + date: string; + tag: string; + commit: string; + source: string; + blocks: Block[]; +}; + +export type Note = Block & { tag: string; source: string; href: string }; + +/** + * One block of a release's notes, found by a substring it contains, as + * scripts/notes.mjs read it out of CHANGELOG.md at the release's tag. + * + * The substring has to pick out exactly one block. Two would mean the post + * could be quoting either, and none means the notes do not say what the post + * wanted them to, which in both cases is the post's mistake to fix, not + * something to guess past. + */ +export function releaseNote(version: string, find: string): Note { + const section = (notes.releases as Record)[version]; + if (section === undefined) { + throw new Error( + `ReleaseNote: no post's frontmatter names ${version}, so scripts/notes.mjs ` + + `did not read its notes. Set \`release: ${version}\` on the post that quotes it.`, + ); + } + const found = section.blocks.filter((block) => block.paragraphs.some((p) => p.includes(find))); + if (found.length !== 1) { + throw new Error( + `ReleaseNote: "${find}" is in ${found.length} blocks of ${version}'s notes at ` + + `${section.tag}, and a quotation has to be one of them.` + + (found.length > 1 ? ` Lines ${found.map((b) => b.from).join(", ")}.` : ""), + ); + } + const [block] = found; + return { + ...block, + tag: section.tag, + source: section.source, + href: `https://github.com/${install.repo}/blob/${section.tag}/${section.source}?plain=1#L${block.from}-L${block.to}`, + }; +} diff --git a/src/pages/blog/[slug].astro b/src/pages/blog/[slug].astro new file mode 100644 index 0000000..0e441a5 --- /dev/null +++ b/src/pages/blog/[slug].astro @@ -0,0 +1,204 @@ +--- +/** + * One post. + * + * The post's prose is its own file under src/content/blog. What this page adds + * is what every post carries and none should have to remember: the date it was + * published, the release it announces, and at the foot the release's notes in + * full and its downloads, both on the repository, where they are kept. + * + * A post is dated, and this page says so in its structured data: Page.astro + * writes `published` as the post's `datePublished`. + */ +import { render } from "astro:content"; +import Page from "../../layouts/Page.astro"; +import H2 from "../../components/H2.astro"; +import H3 from "../../components/H3.astro"; +import { posts, postHref, releaseOf, longDate, type Post } from "../../lib/posts"; +import { releaseUrl } from "../../lib/release"; +import install from "../../generated/install-targets.json"; + +export async function getStaticPaths() { + const all = await posts(); + return all.map((post, i) => ({ + params: { slug: post.id }, + props: { post, older: all[i + 1], newer: i > 0 ? all[i - 1] : undefined }, + })); +} + +interface Props { + post: Post; + older?: Post; + newer?: Post; +} + +const { post, older, newer } = Astro.props; +const { Content } = await render(post); +const base = import.meta.env.BASE_URL; +const indexHref = `${base.replace(/\/$/, "")}/blog/`; +const release = releaseOf(post); +const notes = release + ? `https://github.com/${install.repo}/blob/v${release.version}/CHANGELOG.md` + : undefined; +--- + + +
+
+
+

+ Blog + + + {release && {release.version}} +

+

{post.data.title}

+

{post.data.description}

+
+ +
+ +
+ + { + release && ( + + ) + } + + { + (older || newer) && ( + + ) + } +
+
+
+ + diff --git a/src/pages/blog/index.astro b/src/pages/blog/index.astro new file mode 100644 index 0000000..da25858 --- /dev/null +++ b/src/pages/blog/index.astro @@ -0,0 +1,85 @@ +--- +/** + * The posts, newest first. + * + * A list and nothing else: a date, a title, the post's own description, and + * the release it announces where it announces one. The order and every check + * on a post's frontmatter are src/lib/posts.ts's, so this page cannot list a + * post the post page would refuse to build. + */ +import { Image } from "astro:assets"; +import mascot from "../../assets/mascot.png"; +import Page from "../../layouts/Page.astro"; +import { posts, postHref, longDate } from "../../lib/posts"; + +const base = import.meta.env.BASE_URL; +const all = (await posts()).map((post) => ({ post, href: postHref(base, post) })); +const feed = `${base.replace(/\/$/, "")}/blog/rss.xml`; +--- + + +
+
+

+ + Blog +

+

What changed, and when

+

+ A post for each release: what it changes, what a program written + against the one before has to change, and the release's own notes + quoted from its tag. +

+ +
+ +
+
    + { + all.map(({ post, href }) => ( +
  1. +

    + + {post.data.release && {post.data.release}} +

    +

    + {post.data.title} +

    +

    {post.data.description}

    +
  2. + )) + } +
+
+
+
+ + diff --git a/src/pages/blog/rss.xml.ts b/src/pages/blog/rss.xml.ts new file mode 100644 index 0000000..ec3d8cf --- /dev/null +++ b/src/pages/blog/rss.xml.ts @@ -0,0 +1,62 @@ +/** + * `/blog/rss.xml`: the posts as an RSS 2.0 feed. + * + * Written here rather than through a feed package for the reason llms.txt is: + * it is a few lines of text built from the same list the index renders, and a + * dependency would be a second opinion about that list. Each item carries the + * post's title, link, description and date, and nothing a reader's client + * would have to trust this site to have rendered: the post is the page. + */ +import type { APIRoute } from "astro"; +import { posts } from "../../lib/posts"; + +const escape = (text: string): string => + text + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """); + +/* RFC 822, which RSS 2.0 requires: `Mon, 09 Nov 2026 00:00:00 GMT`. A post + * states a day and no time, so midnight UTC of that day. */ +const rfc822 = (date: string): string => new Date(`${date}T00:00:00Z`).toUTCString(); + +export const GET: APIRoute = async ({ site }) => { + const origin = (site ?? new URL("https://iyi.dev")).href.replace(/\/$/, ""); + const base = import.meta.env.BASE_URL.replace(/\/$/, ""); + const home = `${origin}${base}/blog/`; + const all = await posts(); + + const items = all + .map((post) => { + const link = `${home}${post.id}/`; + return [ + " ", + ` ${escape(post.data.title)}`, + ` ${link}`, + ` ${link}`, + ` ${rfc822(post.data.date)}`, + ` ${escape(post.data.description)}`, + " ", + ].join("\n"); + }) + .join("\n"); + + const latest = all[0]?.data.date; + const xml = ` + + + iyi blog + ${home} + + News from the iyi programming language: what each release changes. + en +${latest ? ` ${rfc822(latest)}\n` : ""}${items} + + +`; + + return new Response(xml, { + headers: { "Content-Type": "application/rss+xml; charset=utf-8" }, + }); +}; diff --git a/src/pages/index.astro b/src/pages/index.astro index e0621fe..2338cdf 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -12,10 +12,17 @@ import { tourSamples, runnable, playgroundHref } from "../playground/samples"; import { latestRelease, releaseAssets, windowsAsset } from "../lib/release"; import platforms from "../generated/targets.json"; import { SPELLED } from "../lib/lessons"; +import { announcementOf, postHref } from "../lib/posts"; import mascot from "../assets/mascot.png"; const release = latestRelease(); +/* The post that announces the current release, when there is one, linked + * under the install button: the release a reader is about to install, and + * what it changed. No post, no line, rather than a link to the blog's index + * standing in for an announcement nobody wrote. */ +const announcement = await announcementOf(release); + /* The tour's first program, what it printed, and the route to it, all taken * from the tour's own table of contents. The page says "the first program of * the tour", so it follows the tour rather than naming a sample: the id and @@ -94,6 +101,13 @@ const windows = windowsAsset(release); Install {release.version} Try it in the browser + { + announcement && ( + + What {release.version} changed → + + ) + } {/* The mascot, large, beside the one line. The largest thing painted above @@ -350,6 +364,10 @@ const windows = windowsAsset(release); background: var(--brand); } + .news { + align-self: flex-start; + } + .run-here { text-decoration: none; } diff --git a/src/pages/learn/[...slug].astro b/src/pages/learn/[...slug].astro index a9b9425..6d7dd17 100644 --- a/src/pages/learn/[...slug].astro +++ b/src/pages/learn/[...slug].astro @@ -23,7 +23,7 @@ * * Two things the markdown pipeline leaves undone are done here. It slugs every * heading and emits nothing that links to a slug, so the `##` and `###` of a - * lesson are mapped onto `_Heading.astro`, which hangs a link on the id the + * lesson are mapped onto `components/Heading.astro`, which hangs a link on the id the * pipeline already put there. And the same slugs are listed in the rail as the * step's own contents, read off the rendered entry, so the list and the page * cannot drift. No reading time anywhere: doc/ART-DIRECTION.md forbids a time @@ -37,8 +37,8 @@ import { render } from "astro:content"; import Page from "../../layouts/Page.astro"; import BreakRule from "../../components/BreakRule.astro"; -import H2 from "./_H2.astro"; -import H3 from "./_H3.astro"; +import H2 from "../../components/H2.astro"; +import H3 from "../../components/H3.astro"; import { sequence, rulesOf, diff --git a/src/pages/learn/_H2.astro b/src/pages/learn/_H2.astro deleted file mode 100644 index 9d15027..0000000 --- a/src/pages/learn/_H2.astro +++ /dev/null @@ -1,8 +0,0 @@ ---- -/** The `##` of a lesson, mapped onto the anchored heading beside this file. */ -import Heading from "./_Heading.astro"; - -const { id } = Astro.props; ---- - - diff --git a/src/pages/learn/_H3.astro b/src/pages/learn/_H3.astro deleted file mode 100644 index c48589a..0000000 --- a/src/pages/learn/_H3.astro +++ /dev/null @@ -1,8 +0,0 @@ ---- -/** The `###` of a lesson, mapped onto the anchored heading beside this file. */ -import Heading from "./_Heading.astro"; - -const { id } = Astro.props; ---- - - diff --git a/src/pages/llms.txt.ts b/src/pages/llms.txt.ts index b77b388..be15e8c 100644 --- a/src/pages/llms.txt.ts +++ b/src/pages/llms.txt.ts @@ -145,6 +145,7 @@ an iyi module; iyi consumes Crystal, not the other way around. - [Download](${origin}/download/): every artifact, the \`uname\` pair it is for, and how to verify it. - [Targets](${origin}/targets/): what CI type-checks, audits and runs, per triple. - [Project](${origin}/project/): contributing, security, the code of conduct, the notices. +- [Blog](${origin}/blog/): a post for each release, what it changes and what a user has to change; RSS at ${origin}/blog/rss.xml. The design record and the release notes live in the repository: SPEC.md and CHANGELOG.md are read there, which is where they are kept current.
+ {note.paragraphs.map((paragraph) =>

)} + {note.source} at {note.tag}, {lines} +