From ef3a08791b4ab7e0d4cb77f390e3b97804b4805c Mon Sep 17 00:00:00 2001 From: Serdar Dogruyol <990485+sdogruyol@users.noreply.github.com> Date: Wed, 21 Oct 2026 17:00:59 +0300 Subject: [PATCH 1/2] publish the standard library reference under /reference/, recorded from iyi doc at the release Every module directly under src/std and every type iyi doc prelude lists gets a page: the module's header comment out of the source at the tag, then each declaration under its own heading with its doc comment and its listing as iyi doc printed it. npm run record:stdlib writes records/stdlib.json; scripts/stdlib.mjs refuses it when the release ships a module it does not cover or a file it was not printed from. requireReleaseCompiler accepts the seven-digit hash the release's own binary prints, so the record can be taken with an unpacked release. prose.mjs exempts doc-comment blocks, which reach the page as one string and carry RFC 6265bis, 0xc0 and 1xx. --- README.md | 17 +- package.json | 5 +- records/stdlib.json | 873 +++++++++++++++++++++++ scripts/budget.json | 5 +- scripts/prose.mjs | 11 +- scripts/record-stdlib.mjs | 159 +++++ scripts/release-ref.mjs | 7 + scripts/stdlib.mjs | 351 +++++++++ src/components/ReferenceBody.astro | 184 +++++ src/lib/reference.ts | 90 +++ src/pages/llms.txt.ts | 2 + src/pages/reference/index.astro | 108 +++ src/pages/reference/prelude/[type].astro | 55 ++ src/pages/reference/std/[module].astro | 60 ++ 14 files changed, 1920 insertions(+), 7 deletions(-) create mode 100644 records/stdlib.json create mode 100644 scripts/record-stdlib.mjs create mode 100644 scripts/stdlib.mjs create mode 100644 src/components/ReferenceBody.astro create mode 100644 src/lib/reference.ts create mode 100644 src/pages/reference/index.astro create mode 100644 src/pages/reference/prelude/[type].astro create mode 100644 src/pages/reference/std/[module].astro diff --git a/README.md b/README.md index 8f4ffe9..625dd63 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,10 @@ cite exists, in this tree or in iyi's. `records/` holds what only a machine with the compiler can produce: the wasm modules the playground runs, the diagnostics the break-this-rule exercises -show, the agent loop the agents page is a transcript of, and the syntax -highlighting of every listing. They are committed, and they are evidence about -**a release**, not about somebody's checkout. +show, the agent loop the agents page is a transcript of, the syntax +highlighting of every listing, and the standard library's surface the +reference under `/reference/` is cut from. They are committed, and they are +evidence about **a release**, not about somebody's checkout. That is what makes them checkable without making the build brittle. Every recorder refuses to run against anything but the tag CHANGELOG.md states, and @@ -75,6 +76,16 @@ target README.md publishes, and wasi-sdk renamed that sysroot to `wasm32-wasip1` and dropped the old name afterwards. `record:wasm` refuses one without that sysroot by name rather than failing at the linker. +`records/stdlib.json` is what `iyi doc` printed for every module directly +under `src/std/` and every type `iyi doc prelude` lists, with the digest of +each source. `scripts/stdlib.mjs` refuses it on the build when the release +ships a module it does not cover or a file it was not printed from, then reads +each module's header comment out of the tag (no compiler output carries it) +and writes `src/generated/stdlib.json`. `record:stdlib` needs only the +compiler, so the release's own binary will do: point `IYI_BUILD` at the `bin/` +of an unpacked release, `iyi` or `iyi.exe`, and `IYI_REPO` at a worktree at +its tag. + `records/agent/` is the second thing this repository authors in iyi: four modules, left broken on purpose, that `record:agent` takes to green with the compiler's own verbs and keeps every frame of. That recorder is also the gate diff --git a/package.json b/package.json index 1cddfa8..1c27121 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,14 @@ "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/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/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/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", "record:highlight": "node scripts/record-highlight.mjs", "record:agent": "node scripts/record-agent.mjs", - "record": "npm run record:wasm && npm run record:diagnostics && npm run record:agent && npm run record:highlight", + "record:stdlib": "node scripts/record-stdlib.mjs", + "record": "npm run record:wasm && npm run record:diagnostics && npm run record:agent && npm run record:highlight && npm run record:stdlib", "predev": "npm run generate", "dev": "astro dev", "prebuild": "npm run generate", diff --git a/records/stdlib.json b/records/stdlib.json new file mode 100644 index 0000000..70324cd --- /dev/null +++ b/records/stdlib.json @@ -0,0 +1,873 @@ +{ + "recorded": { + "compiler": "iyi 0.18.0 (built on Crystal 1.22.0-dev [2a96530] (2026-10-18))", + "commit": "2a9653052ab76f9d8084da47e059273f6ef45228", + "machine": "Windows_NT 10.0.26200 x64, AMD Ryzen AI 9 465 w/ Radeon 880M", + "command": "npm run record:stdlib", + "when": "2026-10-21T13:45:42.974Z" + }, + "modules": [ + { + "name": "std/aead", + "path": "src/std/aead.iyi", + "sourceSha256": "cab5d3927d0d986de337812219eb2c30766d1814d11f99510cf1f8c5df445b8c", + "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" + }, + { + "name": "std/annotations", + "path": "src/std/annotations.iyi", + "sourceSha256": "8db25643edf748ca313456699d1cd5797825be6b4167ae7d0d60d745a089efa0", + "surface": "module std/annotations\n\npub alias Deprecated = ::Deprecated\n\npub alias Experimental = ::Experimental\n\npub alias Flags = ::Flags\n\npub alias Link = ::Link\n\npub alias TargetFeature = ::TargetFeature\n" + }, + { + "name": "std/asn1", + "path": "src/std/asn1.iyi", + "sourceSha256": "7075c10aad9a3588994e26f1624a685fc0b61b4010d11fe0d8ac5dfc30981bda", + "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", + "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/base32", + "path": "src/std/base32.iyi", + "sourceSha256": "f9b1992e89458803b9a096d9855dcb689258b6439f0f439499bc36f2e320dc4b", + "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", + "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" + }, + { + "name": "std/benchmark", + "path": "src/std/benchmark.iyi", + "sourceSha256": "3e0df4d8b10a476f68c7c9b1e1bd01e4e64fbde5bf0ac1f5dead1cdacc2f24d5", + "surface": "module std/benchmark\n\npub class Benchmark\n module BM\n class Job\n def execute : Nil\n def initialize\n def report(label : String = \" \", &block : (->)) : Nil\n end\n class Tms\n def initialize(real : Float64, label : String = \"\")\n def label : String\n def real : Float64\n def to_s(io) : Nil\n def to_s : String\n end\n end\n module IPS\n class Entry\n def action : (->)\n def action=(value : (->)) : (->)\n def calculate_stats(samples : Array(Float64)) : Nil\n def call : Nil\n def call_for_100ms : Nil\n def cycles : Int32\n def cycles=(value : Int32) : Int32\n def human_compare : String\n def human_iteration_time : String\n def human_mean : String\n def initialize(label : String, action : (->))\n def label : String\n def label=(value : String) : String\n def mean : Float64\n def mean=(value : Float64) : Float64\n def ran? : Bool\n def relative_stddev : Float64\n def relative_stddev=(value : Float64) : Float64\n def set_cycles(duration : Std::Time::Span, iterations : UInt64) : Nil\n def size : Int32\n def size=(value : Int32) : Int32\n def slower : Float64\n def slower=(value : Float64) : Float64\n def stddev : Float64\n def stddev=(value : Float64) : Float64\n def variance : Float64\n def variance=(value : Float64) : Float64\n end\n class Job\n def execute : Nil\n def initialize(calculation : Std::Time::Span = Std::Time::Span.seconds(5), warmup : Std::Time::Span = Std::Time::Span.seconds(2), interactive : Bool = false)\n def items : Array(Entry)\n # Seconds as numbers, as the other library takes them. It asked\n # `Time.seconds`, which `Time` (an instant) does not have, so\n # `Job.new(1, 1)` did not compile; a length of time is a `Span`. The\n # answer is named: a program built from this module's artifact read\n # a method with none as one that answers nil.\n def self.new(calculation = 5, warmup = 2, interactive = false) : Job\n def report(label : String = \"\", &action : (->)) : Entry\n def report : Nil\n end\n end\n def self.bm(& : (BM::Job -> Nil)) : BM::Job\n def self.ips(calculation : Std::Time::Span = Std::Time::Span.seconds(5), warmup : Std::Time::Span = Std::Time::Span.seconds(2), interactive : Bool = false, & : (IPS::Job -> Nil)) : IPS::Job\n def self.ips(calculation : Int32 = 5, warmup : Int32 = 2, interactive : Bool = false, & : (IPS::Job -> Nil)) : IPS::Job\n def self.measure(label : String = \"\", & : (-> Nil)) : BM::Tms\n def self.realtime(& : (-> Nil)) : Std::Time::Span\nend\n\npub alias Span = Std::Time::Span\n\npub alias Time = Std::Time::Time\n" + }, + { + "name": "std/big", + "path": "src/std/big.iyi", + "sourceSha256": "5c1a303eb3115206ae3f49ce6881a9722ae2297e58b9fdc6cd41f23f66aa58cb", + "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 # 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", + "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" + }, + { + "name": "std/bool", + "path": "src/std/bool.iyi", + "sourceSha256": "f522ab2941a7fcf09e2e421a42e04ceeafd4510b26e72eeaa46622cc71fe0d06", + "surface": "module std/bool\n\nstruct ::Bool\n # `self` and `other`, both evaluated.\n def &(other : Bool) : Bool\n # Exactly one of the two.\n def ^(other : Bool) : Bool\n def clone : Bool\n # Two different numbers, so `{true => .., false => ..}` has two keys.\n def hash : Int32\n def to_s(io : IyiIO) : Nil\n # 1 for true, 0 for false.\n def to_unsafe : Int32\n # `self` or `other`, both evaluated.\n def |(other : Bool) : Bool\nend\n" + }, + { + "name": "std/box", + "path": "src/std/box.iyi", + "sourceSha256": "f34ecb7faa9c79a4035274e255ae6ce0231a97a27be6f2954acb1ab23e47c9a8", + "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": "33335a7b9f4b7cf41941e3e0a8f6d0f4f9d752f303ab85ef89a126b7ded704cb", + "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", + "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" + }, + { + "name": "std/colorize", + "path": "src/std/colorize.iyi", + "sourceSha256": "bd76185f70e35989e3cc28463e18aafc79f2eb56e57f7911802b068bc75942cc", + "surface": "module std/colorize\n\npub struct Colorize\n alias Color = ColorANSI | Color256 | ColorRGB\n struct Color256\n def initialize(value : UInt8)\n def to_u8 : UInt8\n def value : UInt8\n end\n enum ColorANSI : Int32\n Default = 39\n Black = 30\n Red = 31\n Green = 32\n Yellow = 33\n Blue = 34\n Magenta = 35\n Cyan = 36\n LightGray = 37\n DarkGray = 90\n LightRed = 91\n LightGreen = 92\n LightYellow = 93\n LightBlue = 94\n LightMagenta = 95\n LightCyan = 96\n White = 97\n end\n struct ColorRGB\n def blue : UInt8\n def green : UInt8\n def initialize(red : UInt8, green : UInt8, blue : UInt8)\n def red : UInt8\n end\n enum Mode : Int32\n Default = 0\n Bold = 1\n Dim = 2\n Underline = 4\n Blink = 5\n Reverse = 7\n Hidden = 8\n end\n struct Object(T)\n def back(r : UInt8, g : UInt8, b : UInt8) : self\n def back(color : Color) : self\n def back(color : Symbol) : self\n def back(val : UInt8) : self\n # Colors\n def black : self\n def blink : self\n def blue : self\n # Modes\n def bold : self\n def bright : self\n def cyan : self\n def dark_gray : self\n def default : self\n def dim : self\n def fore(r : UInt8, g : UInt8, b : UInt8) : self\n def fore(color : Color) : self\n def fore(color : Symbol) : self\n def fore(val : UInt8) : self\n def green : self\n def hidden : self\n def initialize(target : T, fore : Color | ::Nil = nil, back : Color | ::Nil = nil, mode : Mode = Mode::Default, enabled : Bool = true)\n def inspect(io) : Nil\n # The target written down, painted: `\"hi\".colorize.red.inspect` is the\n # escape around `\"\\\"hi\\\"\"`, the way the other library has it. It was\n # `to_s`, so a coloured string inspected lost its quotes and read as a\n # name.\n def inspect : String\n def light_blue : self\n def light_cyan : self\n def light_gray : self\n def light_green : self\n def light_magenta : self\n def light_red : self\n def light_yellow : self\n def magenta : self\n def mode(mode : Mode) : self\n # `:bright` is `:bold`, as the `bright` method is; a name that is no mode\n # raises rather than answering `Default`, which cleared the mode already\n # set: `bold.mode(:italic)` printed the text plain.\n def mode(sym : Symbol) : self\n def on(color) : self\n # Backgrounds\n def on_black : self\n def on_blue : self\n def on_cyan : self\n def on_dark_gray : self\n def on_default : self\n def on_green : self\n def on_light_blue : self\n def on_light_cyan : self\n def on_light_gray : self\n def on_light_green : self\n def on_light_magenta : self\n def on_light_red : self\n def on_light_yellow : self\n def on_magenta : self\n def on_red : self\n def on_white : self\n def on_yellow : self\n def red : self\n def reverse : self\n def to_s(io) : Nil\n def to_s : String\n def toggle(enabled : Bool) : self\n def underline : self\n def white : self\n def yellow : self\n end\n module ObjectExtensions\n def colorize(r : UInt8, g : UInt8, b : UInt8) : Colorize::Object(self)\n def colorize(fore : Symbol) : Colorize::Object(self)\n def colorize(fore : Color) : Colorize::Object(self)\n def colorize(fore : UInt8) : Colorize::Object(self)\n def colorize : Colorize::Object(self)\n end\n def self.enabled : Bool\n def self.enabled=(value : Bool) : Bool\n def self.enabled? : Bool\n # The environment's answer, read once: standard output and standard\n # error must both be terminals, `NO_COLOR` set to anything but `\"\"`\n # refuses, `TERM=dumb` refuses, and nothing else is asked. The terminal\n # test is first because it is the one a reader cannot set: escapes in a\n # file are a defect in every environment, and this module wrote them\n # there until the prelude could answer the question (`__iyi_isatty`).\n # Both, as Crystal's `on_tty_only!` asks, because the answer is one for\n # the program and text is painted before anyone knows which stream it\n # goes to: with only standard output asked, `prog 2> err.log` from a\n # terminal wrote escapes into err.log.\n def self.environment_allows? : Bool\n def initialize\n def self.reset(io : IO) : Nil\n def self.reset : Nil\n def self.with : Object(String)\nend\n\nclass ::Object\nend\n" + }, + { + "name": "std/complex", + "path": "src/std/complex.iyi", + "sourceSha256": "dfd3f632b9caccd6de3c236d1987caaead07e1fd9a94932ab3fdd80844ca9ecb", + "surface": "module std/complex\n\npub struct Complex\n def *(other : Complex) : Complex\n def *(other : ::Number) : Complex\n def +(other : Complex) : Complex\n def +(other : ::Number) : Complex\n def + : Complex\n def -(other : Complex) : Complex\n def -(other : ::Number) : Complex\n def - : Complex\n # Smith's scaled quotient, as the other library divides: through\n # `abs2`, a divisor past 1e154 squared to infinity and gave 0, and one\n # under 1e-162 squared to 0 and panicked \"Division by zero\". A zero\n # divisor answers what a float quotient does, infinities and NaN.\n def /(other : Complex) : Complex\n # A zero divisor is a float quotient's infinities and NaN, not a panic.\n def /(other : ::Number) : Complex\n def ==(other : Complex) : ::Bool\n def ==(other : ::Number) : ::Bool\n def abs : Float64\n def abs2 : Float64\n def self.additive_identity : Complex\n def clone : Complex\n # Negated, not subtracted from 0.0: `0.0 - 0.0` is `+0.0`, so the\n # conjugate of `-1 + 0i` sat above the branch cut and its log was `+pi i`.\n def conj : Complex\n # A real exponent keeps its imaginary zero: `r * sin(0.0)` was NaN once\n # `r` overflowed, so e^710 and e^Infinity were `Infinity + NaNi`.\n def exp : Complex\n # The parts' own hashes, the imaginary one left out when it is zero as\n # the other library does, so `Complex.new(x, 0)` hashes as `x`, which it\n # equals. `to_i64` of a part panicked \"arithmetic overflow\" past 2^63 or\n # at NaN and infinity, and `to_i32` of that past 2^31.\n def hash : Int32\n def imag : Float64\n def initialize(real : ::Number, imag : ::Number = 0)\n def inspect(io : IyiIO) : ::Nil\n def inspect : String\n # `conj / abs2`, as the other library inverts, wherever `abs2` is a\n # normal float; past that it overflowed or underflowed, and the inverse\n # of 1e-170 was `Infinity + NaNi` and of 1e170 `0.0 - 0.0i`. There the\n # conjugate is scaled as the division scales it (Smith's), which keeps\n # its zeros' signs.\n def inv : Complex\n def log : Complex\n def self.multiplicative_identity : Complex\n def self.new(c : Complex) : Complex\n def phase : Float64\n def polar : ::Tuple(Float64, Float64)\n def real : Float64\n def round(digits : Int32 = 0) : Complex\n # The other library's cases, one by one: dividing by `abs` answered NaN\n # for an infinite part, 0 once `abs` overflowed (both parts 1.5e308),\n # and a magnitude of 1.414 for two subnormal parts, whose `abs` rounds.\n def sign : Complex\n # A part past an eighth of `Float64::MAX` is rooted a sixteenth of the\n # way down and the root doubled twice: `2.0 * (r + @real)` overflowed,\n # and the root of 1e308 + 1e308i was `Infinity + 0.0i`. A power of two\n # scales each step exactly, so the root is the one the steps would\n # answer had they not overflowed.\n def sqrt : Complex\n def to_c : Complex\n def to_f : Float64\n def to_f32 : Float32\n def to_f64 : Float64\n def to_i32 : Int32\n def to_i64 : Int64\n def to_s(io : IyiIO) : ::Nil\n # The joiner is the imaginary part's sign bit and its magnitude follows,\n # as the other library prints: comparing `>= 0.0` printed `1.0 + -0.0i`\n # for a negative zero and `1.0 - NaNi` for NaN.\n def to_s : String\n def self.zero : Complex\n def zero? : ::Bool\nend\n\nstruct ::Number\n def *(other : Std::Complex::Complex) : Std::Complex::Complex\n def +(other : Std::Complex::Complex) : Std::Complex::Complex\n def -(other : Std::Complex::Complex) : Std::Complex::Complex\n # Times the inverse, as the other library divides a number by a complex:\n # dividing `Complex.new(self, 0)` rounded otherwise, and panicked\n # \"Division by zero\" once the divisor's `abs2` underflowed to 0.\n def /(other : Std::Complex::Complex) : Std::Complex::Complex\n # `1.0 == Complex.new(1, 0)` was false while the reverse was true.\n def ==(other : Std::Complex::Complex) : ::Bool\n # Spelled in full: this file's import of `Math` does not reach the\n # reopened `::Number`, so `0.5.cis` failed to compile (\"undefined\n # constant Math\") unless the caller had imported `Math` itself.\n def cis : Std::Complex::Complex\n def i : Std::Complex::Complex\n def to_c : Std::Complex::Complex\nend\n" + }, + { + "name": "std/compress", + "path": "src/std/compress.iyi", + "sourceSha256": "e0bdcae3d05fb329e3c1561ac6b51291081f287121309e0c313ca2d342fc603b", + "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", + "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", + "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", + "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": "ef68e5e6969424cf301ccc3a47001434d64f18f4544afc429266e5c6c760a7c9", + "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 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", + "surface": "module std/debug\n" + }, + { + "name": "std/deque", + "path": "src/std/deque.iyi", + "sourceSha256": "3c10d9b4c1cb53f818d290d48773cb05cc5138909db71a1492dc0c74dee8c21e", + "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" + }, + { + "name": "std/derives", + "path": "src/std/derives.iyi", + "sourceSha256": "af859a981ce1dd4466ee27ebd024b520bb42d2875d56f5702bedf9aafbc79b5b", + "surface": "module std/derives\n\n# Renders a value as its type name and its fields. One macro, any declaration:\n# the fields come from whichever type the derive is written in.\npub macro described(declaration)\n\n# The question II.4 is about: whether a field's type implements a trait. Only\n# the fields that answer yes are rendered, and each one is rendered by calling\n# the trait's method. A field whose type this macro cannot see answers `nil`.\npub macro shown(declaration)\n\n# A type this module exports, implementing a trait this module exports. A derive\n# in another module gets to ask about both, and both arrive from this module's\n# `.iyimod` when its source is gone.\npub struct Name\n def initialize(text : String)\nend\n\npub trait Show\n abstract def show : String\nend\n\nimpl Show for Name\n def show : String\nend\n" + }, + { + "name": "std/digest", + "path": "src/std/digest.iyi", + "sourceSha256": "624bfb79129f860695808e4f3c95109132a59c57fce35f67ce6eefb86927afe9", + "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", + "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 # The search pattern FindFirstFileW is opened with: the directory, wide,\n # with a separator and a `*` behind it. The directory is converted on its\n # own and the wildcard appended to the result, not converted with it,\n # because a long path's conversion runs through `GetFullPathNameW` and\n # that is a path normaliser rather than a pattern one — the `\\\\?\\` prefix\n # it produces is what a long directory needs here, and a `*` is no\n # business of its. A null pointer is the conversion having failed, which\n # the callers read as the directory not opening.\n protected def self.find_pattern(dir_path : String) : ::Pointer(UInt16)\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" + }, + { + "name": "std/dns", + "path": "src/std/dns.iyi", + "sourceSha256": "bca672855bf97b25347fe10935098a3c2c6eaee988950470166ddf6c3a065875", + "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 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 def initialize(id : UInt16, flags : Int32, question_name : String, question_type : Int32, question_class : Int32, answers : Array(DnsRecord))\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. Anything that does not fit the\n # bytes is `DnsError` with `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 def self.query(id : UInt16, name : String, type : Int32) : 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 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 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\nimpl Error for DnsError\n def message : String\nend\n" + }, + { + "name": "std/ecdsa", + "path": "src/std/ecdsa.iyi", + "sourceSha256": "f03b7a2eb1d7e257fb04f8a0a2cd787550f4f31ea2b1fd7ea1a212fafc1c0317", + "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", + "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" + }, + { + "name": "std/enum", + "path": "src/std/enum.iyi", + "sourceSha256": "96d0838f087510b877b1b8da6a42efd84ec17433ded5a34f90bcc881815a6afd", + "surface": "module std/enum\n\nstruct ::Enum\n # The member `other` places after this one; may name no member.\n def +(other : Int) : self\n # The member `other` places before this one; may name no member.\n def -(other : Int) : self\n # -1, 0 or 1 by value.\n def <=>(other : self) : Int32\n # The bits in exactly one of the two.\n def ^(other : self) : self\n def clone : self\n # Every member in the order written, as `values` lists them: for a flags\n # enum, without `None` and `All`.\n def self.each(& : (self -> Nil)) : Nil\n # The member numbered `val`; panics naming the value where no member is.\n def self.from_value(val : Int) : self\n def inspect(io : IyiIO) : Nil\n # The member named `string`, compared the lenient way `parse?` compares;\n # panics naming the text where no member is.\n def self.parse(string : String) : self\n def to_f64 : Float64\n def to_i : Int32\n def to_i32 : Int32\n def to_i64 : Int64\n def to_s(io : IyiIO) : Nil\n def to_u64 : UInt64\n def to_u8 : UInt8\n # Whether `val` is a member: what `from_value?` answers, as a Bool. For a\n # flags enum that is a single member or `None`; a combination and `All`\n # are not members.\n def self.valid?(val : self) : Bool\nend\n" + }, + { + "name": "std/enumerable", + "path": "src/std/enumerable.iyi", + "sourceSha256": "b65e968dc0cc48f8d52f3a433750c2720ad4153cf26f3446391102828bb65c67", + "surface": "module std/enumerable\n\npub trait Enumerable\n type Elem\n def accumulate(initial : U, & : (U, Elem -> U)) : Array(U) forall U\n def all?(& : (Elem -> Bool)) : Bool\n def any?(& : (Elem -> Bool)) : Bool\n # The same flag as `each_cons_pair`: with the nil test, a nil element\n # read as \"no previous element\", so the block was never asked about the\n # element after it and `[nil, nil, 1]` came back as one chunk.\n def chunk_while(& : (Elem, Elem -> Bool)) : Array(Array(Elem))\n def compact_map(& : (Elem -> U | ::Nil)) : Array(U) forall U\n def count(& : (Elem -> Bool)) : Int32\n def cycle(n : Int32, & : (Elem -> Nil)) : Nil\n abstract def each(& : (Elem -> Nil)) : Nil\n # Yields each overlapping window of *n* elements; nothing when the source\n # is shorter than one window.\n def each_cons(n : Int32, & : (Array(Elem) -> Nil)) : Nil\n def each_cons(n : Int32) : Array(Array(Elem))\n # Whether there is a previous element is a flag, not a nil: an element\n # that is itself nil is still an element, and `[nil, 2]` has the pair\n # `{nil, 2}`. The nil test skipped every pair whose first half was nil.\n def each_cons_pair(& : (Elem, Elem -> Nil)) : Nil\n # Yields each window of *n* elements, the last one short if the source\n # does not divide. The block form is the one `each_` promises everywhere\n # else in this trait — `each`, `each_with_index`, `each_with_object`,\n # `each_cons_pair`, `each_step` all take one — and these two were the\n # exception: they materialised `Array(Array(Elem))` and refused a block,\n # which allocates every window up front, the cost a block exists to\n # avoid. `Indexable` has had both shapes all along.\n def each_slice(n : Int32, & : (Array(Elem) -> Nil)) : Nil\n # And the same windows as an array, for a caller that wants them all.\n def each_slice(n : Int32) : Array(Array(Elem))\n def each_step(n : Int32, & : (Elem -> Nil)) : Nil\n def each_with_index(& : (Elem, Int32 -> Nil)) : Nil\n # The element first and the memo second, as the other library yields them.\n # It was the reverse, so `[[1], [2]].each_with_object([] of Int32) { |x,\n # acc| acc.concat(x) }` compiled under both and answered `[]` here: the\n # element was the one mutated (SPEC.md III.1.7a's worst shape).\n def each_with_object(obj : U, & : (Elem, U -> Nil)) : U forall U\n def empty? : Bool\n def find(& : (Elem -> Bool)) : Elem | ::Nil\n # The first truthy answer, as the other library's: this took the first\n # non-nil one, `false` included, so `[1, 2, 3, 4].find_value { |i| i > 2 }`\n # answered false where the other library documents true for that very\n # call, and `{ |i| i > 8 }` answered false where it answers nil.\n def find_value(& : (Elem -> U | ::Nil)) : U | ::Nil forall U\n def first(n : Int32) : Array(Elem)\n def first : Elem\n def first? : Elem | ::Nil\n def flat_map(& : (Elem -> Array(U))) : Array(U) forall U\n def group_by(& : (Elem -> U)) : Hash(U, Array(Elem)) forall U : Hashable\n # Refused under its own name, as `minmax` below is: delegated, a size of\n # zero was refused with `each_slice`'s sentence, \"slice size must be\n # positive\", from a call the reader never wrote.\n def in_groups_of(n : Int32, filler : Elem) : Array(Array(Elem))\n def in_slices_of(n : Int32) : Array(Array(Elem))\n # Equality, as `Array#includes?` asks it: `<=>` answering 0 is ordering,\n # and for a float it said a NaN was present in a list of NaNs, which no\n # `==` says and `[nan].includes?(nan)` does not. And so no bound: every\n # type has `==`, and the `Comparable` one left from the `<=>` days\n # refused a list of arrays, of nilable numbers or of any class.\n def includes?(value : Elem) : Bool\n def index(& : (Elem -> Bool)) : Int32 | ::Nil\n def index_by(& : (Elem -> U)) : Hash(U, Elem) forall U : Hashable\n # The separator defaults to nothing, the way `Array#join` (src/iyi/array.iyi)\n # and `Indexable#join` already default it: one name means one thing\n # (SPEC.md III.1.7a), and `slice.join` has to compile\n # where `array.join` does. A mandatory separator made the arity of one name\n # depend on which tower the receiver came from.\n #\n # Into one builder: adding each element to the text so far copied that\n # text once per element, and a `List` of 100,000 took nine seconds.\n def join(separator : String = \"\") : String\n def map(& : (Elem -> U)) : Array(U) forall U\n def map_with_index(& : (Elem, Int32 -> U)) : Array(U) forall U\n def max : Elem where Elem : Comparable\n def max? : Elem | ::Nil where Elem : Comparable\n # Whether there was an element is the key's to say, not the element's: a\n # nil element can be the largest, and `found.nil?` refused\n # `List.new([nil, 1]).max_by { |x| x ? x : 5 }` as empty where\n # `Array#max_by` and the other library answer nil. `min_by` and\n # `minmax_by` read the key the same way.\n def max_by(& : (Elem -> U)) : Elem forall U : Comparable\n def max_by?(& : (Elem -> U)) : Elem | ::Nil forall U : Comparable\n def max_of(& : (Elem -> U)) : U forall U : Comparable\n def max_of?(& : (Elem -> U)) : U | ::Nil forall U : Comparable\n def min : Elem where Elem : Comparable\n def min? : Elem | ::Nil where Elem : Comparable\n def min_by(& : (Elem -> U)) : Elem forall U : Comparable\n def min_by?(& : (Elem -> U)) : Elem | ::Nil forall U : Comparable\n def min_of(& : (Elem -> U)) : U forall U : Comparable\n def min_of?(& : (Elem -> U)) : U | ::Nil forall U : Comparable\n # Refuses under its own name rather than letting `min`'s refusal surface\n # from a call the reader never wrote: `minmax` of an empty collection is\n # `minmax`'s mistake, not `min`'s.\n def minmax : Tuple(Elem, Elem) where Elem : Comparable\n # One walk, as `minmax_of?` below takes: `min?` and then `max?` walked the\n # collection twice, so a source that can be read once (a pipe, a\n # generator) answered {1, nil} for [3, 1, 4, 1, 5] and `minmax` refused\n # it as empty.\n def minmax? : Tuple(Elem | ::Nil, Elem | ::Nil) where Elem : Comparable\n def minmax_by(& : (Elem -> U)) : Tuple(Elem, Elem) forall U : Comparable\n def minmax_by?(& : (Elem -> U)) : Tuple(Elem | ::Nil, Elem | ::Nil) forall U : Comparable\n def minmax_of(& : (Elem -> U)) : Tuple(U, U) forall U : Comparable\n def minmax_of?(& : (Elem -> U)) : Tuple(U | ::Nil, U | ::Nil) forall U : Comparable\n def none?(& : (Elem -> Bool)) : Bool\n def one?(& : (Elem -> Bool)) : Bool\n def partition(& : (Elem -> Bool)) : Array(Array(Elem))\n def present? : Bool\n def product : Elem where Elem : Num\n def reduce(initial : U, & : (U, Elem -> U)) : U forall U\n # A flag for \"an element was seen\", not a nil accumulator: a nil element\n # reset the fold, so `[nil, 2]` answered 2 without calling the block.\n def reduce?(& : (Elem, Elem -> Elem)) : Elem | ::Nil\n def reject(& : (Elem -> Bool)) : Array(Elem)\n def select(& : (Elem -> Bool)) : Array(Elem)\n def size : Int32\n def skip(n : Int32) : Array(Elem)\n def skip_while(& : (Elem -> Bool)) : Array(Elem)\n def sorted : Array(Elem) where Elem : Comparable\n def sum : Elem where Elem : Num\n def sum_by(& : (Elem -> U)) : U forall U : Num\n def take(n : Int32) : Array(Elem)\n def take_while(& : (Elem -> Bool)) : Array(Elem)\n def tally : Hash(Elem, Int32) where Elem : Hashable\n def tally_by(& : (Elem -> U)) : Hash(U, Int32) forall U : Hashable\n def to_a : Array(Elem)\n def to_h(& : (Elem -> Tuple(K, V))) : Hash(K, V) forall K : Hashable, V\n def zip(other : O) : Array(Tuple(Elem, O::Elem)) forall O : Enumerable\nend\n\nimpl Enumerable for Array(T) forall T\nend\n\nimpl Enumerable for Set(T) forall T\nend\n" + }, + { + "name": "std/env", + "path": "src/std/env.iyi", + "sourceSha256": "06aec191c04fd2d11460e913ad83152c3fe48262bd53bb23a7a8a91bda74b4dc", + "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" + }, + { + "name": "std/errno", + "path": "src/std/errno.iyi", + "sourceSha256": "210bca36be43688263941b16711ea98017a4cbf2ad6b5b57550db4966fbe6679", + "surface": "module std/errno\n\npub enum Errno : Int32\n NONE = 0\n EPERM = 1\n ENOENT = 2\n ESRCH = 3\n EINTR = 4\n EIO = 5\n ENXIO = 6\n E2BIG = 7\n ENOEXEC = 8\n EBADF = 9\n ECHILD = 10\n ENOMEM = 12\n EACCES = 13\n EFAULT = 14\n ENOTBLK = 15\n EBUSY = 16\n EEXIST = 17\n EXDEV = 18\n ENODEV = 19\n ENOTDIR = 20\n EISDIR = 21\n EINVAL = 22\n ENFILE = 23\n EMFILE = 24\n ENOTTY = 25\n ETXTBSY = 26\n EFBIG = 27\n ENOSPC = 28\n ESPIPE = 29\n EROFS = 30\n EMLINK = 31\n EPIPE = 32\n EDOM = 33\n ERANGE = 34\n EAGAIN = 11\n EWOULDBLOCK = 11\n EDEADLK = 35\n ENAMETOOLONG = 36\n ENOLCK = 37\n ENOSYS = 38\n ENOTEMPTY = 39\n ELOOP = 40\n ENOMSG = 42\n EIDRM = 43\n ESOCKTNOSUPPORT = 94\n EPFNOSUPPORT = 96\n ESHUTDOWN = 108\n ETOOMANYREFS = 109\n ENOSTR = 60\n ENODATA = 61\n ETIME = 62\n ENOSR = 63\n EHOSTDOWN = 112\n ENOLINK = 67\n EUSERS = 87\n EPROTO = 71\n EMULTIHOP = 72\n EBADMSG = 74\n EOVERFLOW = 75\n EILSEQ = 84\n ENOTSOCK = 88\n EDESTADDRREQ = 89\n EMSGSIZE = 90\n EPROTOTYPE = 91\n ENOPROTOOPT = 92\n EPROTONOSUPPORT = 93\n EOPNOTSUPP = 95\n EAFNOSUPPORT = 97\n EADDRINUSE = 98\n EADDRNOTAVAIL = 99\n ENETDOWN = 100\n ENETUNREACH = 101\n ENETRESET = 102\n ECONNABORTED = 103\n ECONNRESET = 104\n ENOBUFS = 105\n EISCONN = 106\n ENOTCONN = 107\n ETIMEDOUT = 110\n ECONNREFUSED = 111\n EHOSTUNREACH = 113\n EALREADY = 114\n EINPROGRESS = 115\n ESTALE = 116\n EDQUOT = 122\n ECANCELED = 125\n EOWNERDEAD = 130\n ENOTRECOVERABLE = 131\n EREMOTE = 66\n def message : String\nend\n" + }, + { + "name": "std/file", + "path": "src/std/file.iyi", + "sourceSha256": "356dadb8c25c92194b4438cb0c8891fc4758042e8c3d8e7c25c58952e94643bb", + "surface": "module std/file\n\npub alias File = ::File\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 # Why the last call failed, in Windows' own words. A message that names\n # only what was attempted — \"Cannot symlink A to B\" — leaves the caller\n # with nothing to act on, and on Windows the difference between \"a\n # required privilege is not held\" and \"the file already exists\" is the\n # difference between a machine setting and a bug in the program. The\n # code is kept beside the text because the text is localised and the\n # number is not.\n #\n # FORMAT_MESSAGE_FROM_SYSTEM is 0x1000 and FORMAT_MESSAGE_IGNORE_INSERTS\n # 0x200: without the second, a message with an insert in it fails rather\n # than printing the placeholder. Windows ends these sentences with CRLF\n # and sometimes a trailing space, which is a line break in the middle of\n # a panic, so the tail comes off. `std/dir`'s refusals say it too.\n def self.win_error : String\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", + "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" + }, + { + "name": "std/fileserver", + "path": "src/std/fileserver.iyi", + "sourceSha256": "7cda46bda2472031341d2daec9bcb34627fd694887a8ba8ed87934ebd1ee802d", + "surface": "module std/fileserver\n\npub struct FileServer\n # One `bytes=` range of a *size*-byte file: its first and last byte,\n # `true` for one that starts past the end (416), nil for anything else\n # - another unit, several ranges, a range that is not one - which is\n # served as the whole file.\n def self.byte_range(value : String, size : Int64) : ::Tuple(Int64, Int64) | Bool | Nil\n def initialize\n # A `Range` is honoured on a GET, and under `If-Range` only when it\n # names this version - its tag, or its date exactly (§13.1.5).\n protected def self.range_applies?(req : Request, tag : String, stamp : String) : Bool\n # The answer to *req* from the files under *root*: what a server's\n # block returns to serve a directory. A GET or HEAD only, 405 otherwise.\n # The path is percent-decoded and read below *root*: a `..` segment, a\n # NUL or a backslash is a 404 rather than a way out of it. A directory\n # is its *index* file, and one asked for without its trailing `/` is\n # redirected there, so relative links inside resolve. The type comes\n # from the extension (std/mime), text with `charset=utf-8`.\n #\n # Caching and ranges are RFC 9110's: `Last-Modified` and an `ETag` made\n # from the modification time and size; `If-None-Match`, and without it\n # `If-Modified-Since`, answer 304. One `Range: bytes=` range is a 206\n # with its `Content-Range`, one past the end a 416; several are served\n # as the whole file (§14.2 lets a server ignore a range), and so is a\n # range whose `If-Range` names another version. The file is read as it\n # is sent, never whole.\n def self.serve(root : String, req : Request, index : String = \"index.html\") : Response\n # Whether an `If-None-Match` list names *tag*, weakly compared (§13.1.2).\n protected def self.tag_listed?(list : String, tag : String) : Bool\nend\n" + }, + { + "name": "std/float", + "path": "src/std/float.iyi", + "sourceSha256": "929e6a66b049e71e229207c040b400e488861e761c29ff168ba86a9375cf63c9", + "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", + "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" + }, + { + "name": "std/heap", + "path": "src/std/heap.iyi", + "sourceSha256": "b5a0634c991c67b1a51aac45483dbb36cd909e66c7e546a14d20689d7160ed92", + "surface": "module std/heap\n\npub class Heap(T)\n def empty? : Bool\n def initialize\n def peek : T where T : Comparable\n def pop : T where T : Comparable\n def push(value : T) : Nil where T : Comparable\n def size : Int32\nend\n" + }, + { + "name": "std/hpack", + "path": "src/std/hpack.iyi", + "sourceSha256": "b360a4741701255597bd44285eab9b6577e63afd86cdc4425f8008b21a3a781c", + "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/html", + "path": "src/std/html.iyi", + "sourceSha256": "c0835a0e91d257c890403854675e0101ae51958f61abe453d812279515af40cc", + "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": "6ee9e23312eb79e5d1a46685ec5e928385b6985b36cb038bbd934a56b781245a", + "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 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 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 host. One more\n # than *redirects* is an `HTTPError`; with 0 the first answer is the\n # 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.\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.\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 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 # *timeout_ms* and *proxy* are `HTTP.open`'s, for every request the\n # client sends.\n def initialize(idle : Int32 = 4, timeout_ms : Int32 = 0, prior_knowledge : Bool = false, proxy : String | ::Nil = nil)\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 # 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.\n protected def take(key : String) : Conn | ::Nil\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# 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.\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 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 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 # 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.\n def self.handle(client : IyiSocket, handler : Proc(Request, Response), listener : IyiSocket | ::Nil = nil, idle : Hash(UInt64, IyiSocket) | ::Nil = nil, stream_bodies : Bool = false, compress : Bool = false, timeout_ms : Int32 = 0, tls : TlsServerConfig | ::Nil = nil) : 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. The block runs on the\n # connection's fiber, once per request; 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 # *tls*: each connection is TLS 1.3 (std/tls's `TlsServer`) under this\n # configuration, its handshake held to *timeout_ms* too; one whose\n # handshake fails is closed, the client told why by an alert. Inside\n # it a connection that opens with HTTP/2's preface - as a client whose\n # ALPN chose `h2` does, when `tls.alpn` offers it - is served HTTP/2,\n # and any other HTTP/1.1. A `Response.upgrade` block over TLS is handed\n # the `TlsStream`, so a WebSocket is `wss://`.\n def self.serve(listener : IyiSocket, stream_bodies : Bool = false, compress : Bool = false, timeout_ms : Int32 = 60000, tls : TlsServerConfig | ::Nil = nil, &handler : (Request -> Response)) : Nil\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 def close : 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 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", + "path": "src/std/http_proxy.iyi", + "sourceSha256": "228c79cf2173ca663a7a3b774c3f57574e595fc38279b640a9c26a56b7b2b0b7", + "surface": "module std/http_proxy\n\npub struct HTTPProxy\n # Whether a variable's name has no case here: Windows'.\n protected def self.folded? : Bool\n # The proxy *url* goes through under the environment, ready for `open`'s\n # *proxy*; nil when it goes direct.\n def self.from_env(url : String) : String | ::Nil\n def initialize\nend\n" + }, + { + "name": "std/idna", + "path": "src/std/idna.iyi", + "sourceSha256": "d4e0bdb1a38a20a56ef6638b39f6533347dec2b8f8bd30fb4a747256b654d693", + "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", + "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", + "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", + "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", + "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" + }, + { + "name": "std/iterator", + "path": "src/std/iterator.iyi", + "sourceSha256": "8c87e79e24de8e78f71e43e2db47cf2e33339522dcf0fd7f3634677c1bc43c90", + "surface": "module std/iterator\n\n# The sources are classes, as the other library's are: an adaptor keeps\n# its source by value, and a struct source 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# the other library gives [1, 2] and [3, 4]. Shared, the adaptor's pulls\n# are the source's. An adaptor that keeps a count, a flag or a buffer is\n# the source of whatever is built on it, so it is a class for the same\n# reason: as a struct it was copied too, and `t = it.take(3)`,\n# `t.first(2).to_a` and then `t.to_a` gave [3, 4, 5] where the other\n# library gives [3], `each_cons` handed its first window out again and\n# `step` and `with_index` restarted their strides and counts. An adaptor\n# that keeps only its sources, a block or a size stays a struct: a copy of\n# it pulls from the same place.\n#\n# Each type defines `next` once and its `impl` names only `Elem`, as\n# `NumericStepIterator` (std/steppable) does: every adaptor carried the same\n# `next` twice, once on the type and once in the `impl`.\npub class ArrayIterator(T)\n def initialize(array : Array(T))\n def next : T | Stop\nend\n\npub class ChainIterator(I1, I2, T)\n def initialize(iter1 : I1, iter2 : I2)\n def next : T | Stop\nend\n\npub struct CompactMapIterator(I, T, U)\n def initialize(iter : I, func : (T -> U | ::Nil))\n def next : U | Stop\nend\n\npub class ConsIterator(I, T)\n def initialize(iter : I, n : Int32)\n def next : Array(T) | Stop\nend\n\npub class CycleIterator(I, T)\n def initialize(iter : I)\n def next : T | Stop\nend\n\npub class CycleNIterator(I, T)\n def initialize(iter : I, n : Int32)\n def next : T | Stop\nend\n\n# The source an endless range gets: it counts up by one from the beginning\n# with no end, as an endless range does in the other library.\npub class EndlessRangeIterator(B)\n def initialize(current : B)\n def next : B\nend\n\npub class EntryIterator(K, V)\n def initialize(hash : Hash(K, V))\n def next : Tuple(K, V) | Stop\nend\n\npub class FlatMapIterator(I, T, U)\n def initialize(iter : I, func : (T -> Array(U)))\n def next : U | Stop\nend\n\npub trait Iterator\n type Elem\n def all?(& : (Elem -> Bool)) : Bool\n # Whether an element is truthy, which is what the other library's `any?`\n # answers: this said whether there was an element at all, so\n # `Iterator.of([false, false]).any?` was true. It pulls until it meets a\n # truthy element, and what it pulled is gone: an iterator is a pull, so a\n # predicate over one cannot be free. For a source with no `false` in it\n # that is one element. The block form below reads as much as it has to\n # and no more. Said here because a method spelled like a question that\n # quietly takes elements is the kind of thing a caller finds out from the\n # answer to the *next* question.\n def any? : Bool\n def any?(& : (Elem -> Bool)) : Bool\n def chain(other : O) : ChainIterator(self, O, Elem | O::Elem) forall O : Iterator\n def compact_map(&func : (Elem -> U | ::Nil)) : CompactMapIterator(self, Elem, U) forall U\n def count : Int32\n def count(& : (Elem -> Bool)) : Int32\n def cycle(n : Int32) : CycleNIterator(self, Elem)\n def cycle : CycleIterator(self, Elem)\n def each(& : (Elem -> Nil)) : Nil\n def each_cons(n : Int32) : ConsIterator(self, Elem)\n def each_slice(n : Int32) : SliceIterator(self, Elem)\n # Nil when nothing matched, which is what `Enumerable#find` and `Array#find`\n # answer: not finding is the ordinary outcome of a search, and a caller who\n # wants the panic writes `|| raise` as the prelude does. `first?`/`first`\n # below keeps its pair — an exhausted iterator has no first element, which\n # is a question with no answer.\n def find(& : (Elem -> Bool)) : Elem | ::Nil\n def first(n : Int32) : TakeIterator(self, Elem)\n def first : Elem\n def first? : Elem | ::Nil\n def flat_map(&func : (Elem -> Array(U))) : FlatMapIterator(self, Elem, U) forall U\n def map(&func : (Elem -> U)) : MapIterator(self, Elem, U) forall U\n def max : Elem where Elem : Comparable\n # The largest, or nil when there is none. `max?`/`max` is a real pair:\n # an empty source has no largest element, so the plain name raising is\n # the common case. This file keeps the same pair for `first?`/`first`,\n # and answers `find` with nil, where not finding is the ordinary outcome.\n def max? : Elem | ::Nil where Elem : Comparable\n def min : Elem where Elem : Comparable\n # And the same pair at the other end.\n def min? : Elem | ::Nil where Elem : Comparable\n abstract def next : Elem | Stop\n def none?(& : (Elem -> Bool)) : Bool\n # Convenience constructor for arrays.\n def self.of(array : Array(T)) : ArrayIterator(T) forall T\n # An endless range counts up for as long as it is pulled, as the other\n # library's does: `Iterator.of((1..)).first(3).to_a` was accepted here\n # and then refused from inside `RangeIterator#next`, \"expected argument\n # #1 to 'Int32#<' to be ... not Nil\", a line of this file. A beginless\n # range has no first element to count from, and is refused at the call.\n def self.of(range : Range(B, Nil)) : EndlessRangeIterator(B) forall B\n def self.of(range : Range(Nil, E)) : Nil forall E\n def self.of(range : Range(B, E)) : RangeIterator(B, E) forall B, E\n def self.of_entries(hash : Hash(K, V)) : EntryIterator(K, V) forall K, V\n def self.of_keys(hash : Hash(K, V)) : KeyIterator(K, V) forall K, V\n def self.of_values(hash : Hash(K, V)) : ValueIterator(K, V) forall K, V\n def product : Elem where Elem : Num\n def reduce(initial : U, & : (U, Elem -> U)) : U forall U\n def reduce?(& : (Elem, Elem -> Elem)) : Elem | ::Nil\n def reject(&func : (Elem -> Bool)) : RejectIterator(self, Elem)\n def select(&func : (Elem -> Bool)) : SelectIterator(self, Elem)\n def size : Int32\n def skip(n : Int32) : SkipIterator(self, Elem)\n def skip_while(&func : (Elem -> Bool)) : SkipWhileIterator(self, Elem)\n def step(n : Int32) : StepIterator(self, Elem)\n def sum : Elem where Elem : Num\n def take(n : Int32) : TakeIterator(self, Elem)\n def take_while(&func : (Elem -> Bool)) : TakeWhileIterator(self, Elem)\n def to_a : Array(Elem)\n def with_index(offset : Int32 = 0) : WithIndexIterator(self, Elem)\n def with_index(offset : Int32 = 0, & : (Elem, Int32 -> Nil)) : Nil\n # Bounded, as `Enumerable#zip` is (SPEC.md II.6 finding 6). Unbounded, a\n # zip with an array was refused from inside this file, \"undefined\n # constant O::Elem\" at the line below, and a chain with one from\n # `ChainIterator#next`; with `forall O : Iterator` the refusal names the\n # caller's line, the argument's type and `O`. A chain of two element\n # types yields either, as the other library's does: it was a return-type\n # error inside `ChainIterator`.\n def zip(other : O) : ZipIterator(self, Elem, O, O::Elem) forall O : Iterator\nend\n\n# The three views of a hash, in insertion order. Each takes its snapshot of\n# the hash once, when built — `Hash#keys` and `Hash#values` allocate a fresh\n# array per call, so asking per element was a copy of the table per step —\n# and then walks that snapshot, so a hash that changes under the iterator\n# does not change what it yields.\npub class KeyIterator(K, V)\n def initialize(hash : Hash(K, V))\n def next : K | Stop\nend\n\npub struct MapIterator(I, T, U)\n def initialize(iter : I, func : (T -> U))\n def next : U | Stop\nend\n\n# Counts up by one from the range's beginning, the way the prelude's\n# `Range#each` does, and stops at the end rather than stepping past it:\n# on an inclusive range whose end is the type's largest value, the step\n# after the last element would overflow.\npub class RangeIterator(B, E)\n def initialize(range : Range(B, E))\n def next : B | Stop\nend\n\npub struct RejectIterator(I, T)\n def initialize(iter : I, func : (T -> Bool))\n def next : T | Stop\nend\n\npub struct SelectIterator(I, T)\n def initialize(iter : I, func : (T -> Bool))\n def next : T | Stop\nend\n\npub class SkipIterator(I, T)\n def initialize(iter : I, n : Int32)\n def next : T | Stop\nend\n\npub class SkipWhileIterator(I, T)\n def initialize(iter : I, func : (T -> Bool))\n def next : T | Stop\nend\n\npub struct SliceIterator(I, T)\n def initialize(iter : I, n : Int32)\n def next : Array(T) | Stop\nend\n\npub class StepIterator(I, T)\n def initialize(iter : I, by : Int32)\n def next : T | Stop\nend\n\npub struct Stop\n def initialize\nend\n\npub class TakeIterator(I, T)\n def initialize(iter : I, n : Int32)\n def next : T | Stop\nend\n\npub class TakeWhileIterator(I, T)\n def initialize(iter : I, func : (T -> Bool))\n def next : T | Stop\nend\n\npub class ValueIterator(K, V)\n def initialize(hash : Hash(K, V))\n def next : V | Stop\nend\n\npub class WithIndexIterator(I, T)\n def initialize(iter : I, offset : Int32)\n def next : Tuple(T, Int32) | Stop\nend\n\npub struct ZipIterator(I1, T1, I2, T2)\n def initialize(iter1 : I1, iter2 : I2)\n # Asks the second source only when the first has an element. Asking both\n # took one from the second each time the first had run out: after\n # `a.zip(b).to_a` with `a` = [1] and `b` = [10, 20, 30], `b.next`\n # answered 30 where the other library and Python's `zip` answer 20.\n def next : Tuple(T1, T2) | Stop\nend\n\nimpl Iterator for EndlessRangeIterator(B) forall B\nend\n\nimpl Iterator for ArrayIterator(T) forall T\nend\n\nimpl Iterator for RangeIterator(B, E) forall B, E\nend\n\nimpl Iterator for KeyIterator(K, V) forall K, V\nend\n\nimpl Iterator for ValueIterator(K, V) forall K, V\nend\n\nimpl Iterator for EntryIterator(K, V) forall K, V\nend\n\nimpl Iterator for MapIterator(I, T, U) forall I, T, U\nend\n\nimpl Iterator for SelectIterator(I, T) forall I, T\nend\n\nimpl Iterator for RejectIterator(I, T) forall I, T\nend\n\nimpl Iterator for TakeIterator(I, T) forall I, T\nend\n\nimpl Iterator for TakeWhileIterator(I, T) forall I, T\nend\n\nimpl Iterator for SkipIterator(I, T) forall I, T\nend\n\nimpl Iterator for SkipWhileIterator(I, T) forall I, T\nend\n\nimpl Iterator for ZipIterator(I1, T1, I2, T2) forall I1, T1, I2, T2\nend\n\nimpl Iterator for ChainIterator(I1, I2, T) forall I1, I2, T\nend\n\nimpl Iterator for FlatMapIterator(I, T, U) forall I, T, U\nend\n\nimpl Iterator for SliceIterator(I, T) forall I, T\nend\n\nimpl Iterator for ConsIterator(I, T) forall I, T\nend\n\nimpl Iterator for WithIndexIterator(I, T) forall I, T\nend\n\nimpl Iterator for CycleIterator(I, T) forall I, T\nend\n\nimpl Iterator for CycleNIterator(I, T) forall I, T\nend\n\nimpl Iterator for StepIterator(I, T) forall I, T\nend\n\nimpl Iterator for CompactMapIterator(I, T, U) forall I, T, U\nend\n" + }, + { + "name": "std/json", + "path": "src/std/json.iyi", + "sourceSha256": "e5388d36116b5e92d043d653b8561cd6016e2092e82af72577df2ca5d3da8ff5", + "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 key the type has no field for is passed over; a field with no\n# key keeps its default when it is written with one, as does a field with\n# a default whose key holds `null` where its type does not admit nil; a\n# field with neither is refused by name unless its type admits `nil`,\n# which it then holds. `@[JSON::Field(...)]` renames a field, leaves it\n# out either way, leaves a nil one out, or reads and writes it through a\n# converter (see `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`, a type with `derive serializable`,\n # one of those or `nil`, or an `Array` or `Hash(String, V)` of those. An\n # integer outside the type is refused, as is anything after the root\n # 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.\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 `Array` or `Hash` of those, or a type\n # that implements `ToJSON`; each of them or `nil`.\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 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" + }, + { + "name": "std/kernel", + "path": "src/std/kernel.iyi", + "sourceSha256": "6905c6a09d5daa1c7870c8706aa6729b482a75a8cd53aac5b9e24415b4a695ed", + "surface": "module std/kernel\n\n# Terminates the program with *status*, after writing *message* to\n# standard error as a line, as `puts` writes one: a message that already\n# ends in a newline gets no second one, as in the other library.\n#\n# The ending is `exit`'s, as the other library's `abort` is its `exit`:\n# what the task deferred runs, and what a stream set to `sync = false`\n# holds is written. This wrote through the runtime's own `__iyi_write` and\n# ended with `__iyi_exit`, which skip both: `STDOUT.sync = false`,\n# `puts \"before\"`, `abort \"bye\"` left standard output empty, a `defer`\n# around the call never ran, and a line buffered on standard error before\n# the message was lost.\npub def abort(message : String | ::Nil = nil, status : Int32 = 1) : NoReturn\n\n# Repeatedly executes the block until break.\npub def loop(& : (-> Nil)) : Nil\n\n# Inspects object to standard output followed by newline. Returns object.\npub def p(object : T) : T forall T\n\n# Inspects each object to standard output. Returns objects tuple.\npub def p(*objects : *T) : Tuple(*T) forall T\n\n# Pretty prints object to standard output. Returns object.\npub def pp(object : T) : T forall T\n\n# Pretty prints each object to standard output. Returns objects tuple.\npub def pp(*objects : *T) : Tuple(*T) forall T\n\n# Prints objects without trailing newline.\npub def print_all(*objects : *T) : Nil forall T\n\n# Prints objects followed by a newline.\npub def puts_all(*objects : *T) : Nil forall T\n" + }, + { + "name": "std/levenshtein", + "path": "src/std/levenshtein.iyi", + "sourceSha256": "dbf6aaec3fcb2b675100ca0bc5a50244a825b7a35b194a6a64cc58d671f28f4a", + "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", + "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" + }, + { + "name": "std/log", + "path": "src/std/log.iyi", + "sourceSha256": "8aabd06e381d1e9e121f042ff38fe26c6548d7a22d96b8f78612a52fd22e3c7f", + "surface": "module std/log\n\n# Entries written by a thread of their own, so a call that logs does not\n# wait for the write: the entry is formatted where it is logged and\n# queued, and a writer thread writes what has queued up since its last\n# write, in order, in one write, to *io*'s descriptor.\n#\n# AsyncBackend.open do |backend| # closed, and drained, at the end\n# Log.setup(Severity::Info, backend)\n# ...\n# end\n#\n# `close` waits for every queued entry to be written and the thread to\n# end; an entry written after it is refused. Entries queued when a program\n# ends without `close` are lost, which the block form rules out. The\n# backend is the thread that made it's, as every object not Share is.\n#\n# A thread needs the collector and a target with threads: under\n# `-Dgc_none` or `-Dgc_boehm`, or on another target, `new` refuses.\npub class AsyncBackend < Backend\n def close : Nil\n def formatter : Formatter\n def formatter=(value : Formatter) : Formatter\n def initialize(io : IO = STDOUT, formatter : Formatter = DefaultFormatter.new)\n # A backend for the block, closed and drained when the block ends.\n def self.open(io : IO = STDOUT, formatter : Formatter = DefaultFormatter.new, &block : (AsyncBackend -> R)) : R forall R\n def write(entry : Entry) : Nil\nend\n\npub abstract class Backend\n abstract def close : Nil\n abstract def write(entry : Entry) : Nil\nend\n\npub class DefaultFormatter < Formatter\n def format(entry : Entry) : String\nend\n\npub struct Entry\n # The task's context, the logger's bound data and the call's own, in\n # that order, a later key's value over an earlier's.\n def data : Hash(String, String)\n def initialize(source : String, severity : Severity, message : String, timestamp : Time = Time.utc, data : Hash(String, String) = {} of String => String)\n def message : String\n def severity : Severity\n def source : String\n def timestamp : Time\nend\n\npub abstract class Formatter\n def format(entry : Entry, io : IO) : Nil\n abstract def format(entry : Entry) : String\nend\n\npub class IOBackend < Backend\n def close : Nil\n def formatter : Formatter\n def formatter=(value : Formatter) : Formatter\n def initialize(io : IO, formatter : Formatter = DefaultFormatter.new)\n def io : IO\n def write(entry : Entry) : Nil\nend\n\n# The root logger and the calls that go straight to it.\npub class Log\n def self.backend : Backend | ::Nil\n def self.backend=(backend : Backend | ::Nil) : Nil\n # The data this task's open contexts give an entry, the outermost first.\n def self.context : Hash(String, String)\n def self.debug(msg : String, **data) : Nil\n def self.debug(**data, &b : (-> String)) : Nil\n def self.error(msg : String, **data) : Nil\n def self.error(**data, &b : (-> String)) : Nil\n def self.fatal(msg : String, **data) : Nil\n def self.fatal(**data, &b : (-> String)) : Nil\n def self.for(source : String) : Logger\n def self.info(msg : String, **data) : Nil\n def self.info(**data, &b : (-> String)) : Nil\n def self.level : Severity\n def self.level=(level : Severity) : Nil\n def self.notice(msg : String, **data) : Nil\n def self.notice(**data, &b : (-> String)) : Nil\n def self.root : Logger\n # Reconfigures the root in place, so loggers obtained earlier follow.\n def self.setup(level : Severity = Severity::Info, backend : Backend = IOBackend.new(STDOUT)) : Logger\n # `setup`, the level read from *variable* - a severity's name, any case\n # - or *default_level* when it is unset or empty. A name that is no\n # severity is refused, the severities named.\n def self.setup_from_env(default_level : Severity = Severity::Info, backend : Backend = IOBackend.new(STDOUT), variable : String = \"LOG_LEVEL\") : Logger\n def self.trace(msg : String, **data) : Nil\n def self.trace(**data, &b : (-> String)) : Nil\n def self.warn(msg : String, **data) : Nil\n def self.warn(**data, &b : (-> String)) : Nil\n # Runs the block with *data* on every entry logged in this task and in\n # the tasks it starts, over the context already open, and answers the\n # block's answer. The context before it is back when the block ends,\n # panic or not.\n def self.with_context(**data, &block : (-> R)) : R forall R\nend\n\npub class Logger\n # This logger's own backend, or the nearest ancestor's; nil silences.\n def backend : Backend | ::Nil\n def backend=(b : Backend | ::Nil) : Nil\n # A logger for the same source, its level and backend this one's, whose\n # entries all carry *data*: a request's id, a connection's peer.\n def bind(**data) : Logger\n # The data `bind` gave this logger and its parents, the root's first.\n protected def bound_into(found : Hash(String, String)) : Nil\n def debug(message : String, **data) : Nil\n def debug(**data, &b : (-> String)) : Nil\n def debug? : Bool\n def error(message : String, **data) : Nil\n def error(**data, &b : (-> String)) : Nil\n def error? : Bool\n def fatal(message : String, **data) : Nil\n def fatal(**data, &b : (-> String)) : Nil\n def fatal? : Bool\n # The logger for *child_name* under this one, created on first ask;\n # `\"a.b\"` is the child `b` of the child `a`.\n def for(child_name : String) : Logger\n protected def get_or_create_child(name : String) : Logger\n def info(message : String, **data) : Nil\n def info(**data, &b : (-> String)) : Nil\n def info? : Bool\n def initialize(source : String, level : Severity | ::Nil = nil, backend : Backend | ::Nil = nil, parent : Logger | ::Nil = nil, bound : Metadata | ::Nil = nil)\n # This logger's own level, or the nearest ancestor's; Info at a root\n # never given one.\n def level : Severity\n # nil hands the decision back to the parent.\n def level=(lvl : Severity | ::Nil) : Nil\n # None is a level and not a severity: an entry logged at None was\n # written by a logger set to None, the one level meant to write nothing.\n def log(severity : Severity, message : String, **data) : Nil\n def log(severity : Severity, **data, &block : (-> String)) : Nil\n def notice(message : String, **data) : Nil\n def notice(**data, &b : (-> String)) : Nil\n def notice? : Bool\n def own_backend : Backend | ::Nil\n def own_level : Severity | ::Nil\n def source : String\n def trace(message : String, **data) : Nil\n def trace(**data, &b : (-> String)) : Nil\n def trace? : Bool\n def warn(message : String, **data) : Nil\n def warn(**data, &b : (-> String)) : Nil\n def warn? : Bool\nend\n\npub class MemoryBackend < Backend\n def clear : Nil\n def close : Nil\n def entries : Array(Entry)\n def formatter : Formatter\n def formatter=(value : Formatter) : Formatter\n def initialize(formatter : Formatter = DefaultFormatter.new)\n # The entries as the formatter would have written them.\n def output : Array(String)\n def write(entry : Entry) : Nil\nend\n\n# Keys and values, the newest first: immutable, so a task's context can sit\n# in the table every thread reads.\npub class Metadata\n def initialize(key : String, value : String, rest : Metadata | ::Nil)\n def key : String\n # Into *into*, the oldest first, so a later key's value replaces an\n # earlier one's.\n def merge_into(into : Hash(String, String)) : Nil\n def rest : Metadata | ::Nil\n def to_h : Hash(String, String)\n def value : String\nend\n\npub enum Severity : Int32\n Trace = 0\n Debug = 1\n Info = 2\n Notice = 3\n Warn = 4\n Error = 5\n Fatal = 6\n None = 7\n def <(other : Severity) : Bool\n def <=(other : Severity) : Bool\n def >(other : Severity) : Bool\n def >=(other : Severity) : Bool\n def label : String\n def self.parse(name : String) : Severity\n # The severity named by *name*, case-insensitively, or nil: what a\n # `LOG_LEVEL` variable holds.\n def self.parse?(name : String) : Severity | ::Nil\n def to_s : String\nend\n" + }, + { + "name": "std/mail", + "path": "src/std/mail.iyi", + "sourceSha256": "ed9455aa36ceb50d7b93788cf2a0110bc59c2c9bad38b8fe26bab97a497d0fb5", + "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.\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": "0e281b320dc9539d1f15984621a425d3ff60da20085b11d4ad811a795b9f74cb", + "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 def self.copysign(mag : Float64, sgn : Float64) : Float64\n def self.copysign(mag : Float32, sgn : Float32) : Float32\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 # The larger, with the number when one side is NaN, and 0.0 over -0.0.\n def self.max(a : Float64, b : Float64) : Float64\n def self.max(a : Float32, b : Float32) : Float32\n # The smaller, with the number when one side is NaN, and -0.0 over 0.0.\n def self.min(a : Float64, b : Float64) : Float64\n def self.min(a : Float32, b : Float32) : Float32\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", + "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/mutex", + "path": "src/std/mutex.iyi", + "sourceSha256": "8171d7e399fd514875a37f775459a9c6ed5b1baa978d0210aa84d07c59d24aa7", + "surface": "module std/mutex\n\npub class Mutex(T)\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 # 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 # Whether this thread has anything to run besides the waiting task:\n # tasks ready to run, tasks asleep, tasks waiting on I/O.\n protected def self.thread_busy? : Bool\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 what it does while it\n # waits is `lock`'s: yields, then sleeps a millisecond at a time, and on\n # Linux a thread with nothing else to run sleeps in the kernel until the\n # next release. Cancellable, as `lock` is. A wait nothing could end - one\n # thread, and nothing else on it to run - panics saying so.\n def wait_for(&block : (T -> R | ::Nil)) : R | Cancelled forall R\nend\n" + }, + { + "name": "std/named_tuple", + "path": "src/std/named_tuple.iyi", + "sourceSha256": "6a743b48229d154810ec5f91dcc7137d645c5e7fa63d16cf5ed023dbc8164233", + "surface": "module std/named_tuple\n\nstruct ::NamedTuple(T)\n # Returns the value for *key*, or panics if missing.\n def [](key : Symbol)\n # Returns the value for string *key*, or panics if missing.\n def [](key : String)\n # Returns the value for *key*, or nil if missing.\n def []?(key : Symbol)\n def []?(key : String)\n # Deep clone of this named tuple, as far as the values allow: each value\n # whose type has `clone` is cloned and any other is kept as it is.\n # Asking every value for `clone` refused `{a: 1, b: \"x\"}`: the prelude\n # gives `clone` to neither `Int32` nor `String`, so only a named tuple of\n # values that all had one compiled.\n def clone\n # Yields each key symbol in order.\n def each_key(& : (Symbol -> Nil)) : Nil\n # Yields each key, value, and integer index.\n def each_with_index(offset : Int = 0) : Nil\n # Fetches value for *key*, returning *default_value* if missing.\n def fetch(key : Symbol, default_value)\n def fetch(key : String, default_value)\n # Fetches value for *key*, or yields to block if missing.\n def fetch(key : Symbol)\n def fetch(key : String)\n # Returns true if the named tuple has the given string key.\n def has_key?(key : String) : Bool\n # Merges another named tuple with self. Entries in *other* overwrite matching keys.\n def merge(other : NamedTuple)\n def merge(**other : **U) forall U\n # Creates a named tuple from keyword options.\n def self.new(**options : **T)\n # Merges self into other: self's values win on overlapping keys.\n def reverse_merge(other : NamedTuple)\n def reverse_merge(**other)\n # Converts this named tuple into an Array of {Symbol, Value} pairs.\n def to_a\n # Converts this named tuple into a Hash.\n def to_h\nend\n" + }, + { + "name": "std/netip", + "path": "src/std/netip.iyi", + "sourceSha256": "86ea9f02692a33d9c50095a7db3ed2b72cb764f7b4386d176b35c8d531521297", + "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", + "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", + "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" + }, + { + "name": "std/option_parser", + "path": "src/std/option_parser.iyi", + "sourceSha256": "b394a198eeaf8219d011c903348a85a66773c12716dc160c19e20e3714f08cca", + "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", + "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": "519791b0842b8d5f787903a32696dd3fc0d54915eb0cf5628817864c8bd5821d", + "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/password", + "path": "src/std/password.iyi", + "sourceSha256": "f29e48291275b0af500b4635f7a1722ac6f425055ff97bb4db16950f5cc4e922", + "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", + "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", + "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": "28cc11c3a78c92f7c2dc62fa229a826e3648ed69eac46b894343f4b68f57da05", + "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": "1ecf78ec9a7fd494377b691adbed94a855bba353e974cb333c65c917022c207c", + "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", + "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": "7b6cbf68745e7ffe3f780415f5442cc6c7db6dd621b87cdc0c1ecf3757d4ea92", + "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 : Array(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": "08330b79870a18dae9c55fd346f503d801db1b9adcaa243cfd081a9b7ea1db16", + "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 : Array(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 : Array(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) { |req| router.call(req) }\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# 301 (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 301, 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 # 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\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\nimpl Error for RouterError\n def message : String\nend\n" + }, + { + "name": "std/rsa", + "path": "src/std/rsa.iyi", + "sourceSha256": "bdfe4106b5fdda512740f693a6ff1d10748670ddb62c0285029f89f830aaddd8", + "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/semantic_version", + "path": "src/std/semantic_version.iyi", + "sourceSha256": "32b113295e8a61d4d6a9c9387b07f521b684f7127f9ee97ef2ac6dfeb8968ff7", + "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" + }, + { + "name": "std/set", + "path": "src/std/set.iyi", + "sourceSha256": "5cc08e55afdde00aa2b041123aaeabc9a86b56095a93fb74ce3d102587eea377", + "surface": "module std/set\n\nclass ::Set(T)\n # Intersection: what is in both, in this set's order.\n def &(other : Set(U)) : Set(T) forall U\n # `+` is union: two sets added are one set.\n def +(other : Set(U)) : Set(T | U) forall U\n # Adds value and answers the set, for chaining: `set << 1 << 2`.\n def <<(value : T) : Set(T)\n # The same members, in any order, where one element type holds the\n # other, as tuples compare (src/iyi/object.iyi).\n def ==(other : Set(U)) : Bool forall U\n # Symmetric difference: what is in exactly one of the two.\n def ^(other : Set(U)) : Set(T | U) forall U\n # Adds every element of other; answers the set.\n def concat(other : Set(T)) : Set(T)\n # No element in common; two empty sets are disjoint.\n def disjoint?(other : Set(U)) : Bool forall U\n # A copy with the same elements in the same order.\n def dup : Set(T)\n # Whether value, of any type, is a member: one this set's type cannot\n # hold is not.\n protected def has_member?(value) : Bool\n # Equal sets hash alike, and unequal ones apart. Each member's hash goes\n # through splitmix64's finaliser before a wrapping sum, which does not\n # care about order the way the sets do not, and the size is folded in\n # after, as `Array#hash` folds it. The members' hashes summed as they\n # were put a set of small integers at their total, `Int32#hash` being the\n # value: {1, 4} and {2, 3} were one key, the 11,175 sets {i, j} with\n # i < j < 150 had 297 hashes between them, and a table of such keys grew\n # quadratically.\n def hash : Int32\n # At least one element in common.\n def intersects?(other : Set(U)) : Bool forall U\n # A subset that is not the same set.\n def proper_subset_of?(other : Set(U)) : Bool forall U\n def proper_superset_of?(other : Set(U)) : Bool forall U\n # Every element of this set is in other. The empty set is a subset of\n # every set, itself included.\n def subset_of?(other : Set(U)) : Bool forall U\n def superset_of?(other : Set(U)) : Bool forall U\n # Union: everything in either, this set's elements first. The other set\n # may be of another element type, as everything below may: the union of\n # a `Set(Int32)` and a `Set(Int32?)` is a `Set(Int32?)`, either way round.\n # Restricted to `Set(T)`, a set of the narrower type was taken for the\n # wider one and not back, and where the body then asked the narrower set\n # about a member of the wider type it failed to compile inside this file:\n # `Set(Int32?) == Set(Int32)`, `&`, `^`, `subset_of?` and the rest, while\n # `Set(Int32) == Set(Int32?)` answered false for the same members.\n def |(other : Set(U)) : Set(T | U) forall U\nend\n" + }, + { + "name": "std/shlex", + "path": "src/std/shlex.iyi", + "sourceSha256": "b42d6ad9063539b09f7026ae3f12512628b927e2813b6272b3e3f9a24e6e740c", + "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": "9dc862b6a18ae292c72c36f482c3bc37efe7af9653b1f388f8d0883128d39a27", + "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" + }, + { + "name": "std/smtp", + "path": "src/std/smtp.iyi", + "sourceSha256": "a683f9c8884d341eec2f4161f47ce2172425ddcc112f434ac872a1c520ba73b9", + "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.\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.\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": "47a6354bf8717cd28c325ac48c333d7e536fb29cf524bc22e18716216db54c53", + "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# 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 alias AcceptExFn = Proc(UInt64, UInt64, ::Pointer(Void), Int32, Int32, Int32, Pointer(Int32), ::Pointer(Void), Int32)\n alias ConnectExFn = Proc(UInt64, ::Pointer(Void), Int32, ::Pointer(Void), Int32, Pointer(Int32), ::Pointer(Void), Int32)\n lib LibAdvapi32\n end\n lib LibWs2_32\n # Two slots of an address and the sixteen bytes after it, room for\n # the widest family this file makes.\n struct AcceptAddresses\n end\n struct PollFd\n end\n # A WSABUF, and the two address slots AcceptEx fills in - each a\n # `sockaddr_in` and the sixteen bytes it insists on after one. They are\n # declared here so that the callers below can keep them on the stack:\n # the kernel reads and writes these while the fiber is parked, and a\n # heap chunk whose only reference is a local the posting call has\n # finished with is a chunk the collector can hand to somebody else\n # first. `bench/server_load.iyi` died of a memory fault on exactly\n # that, with the accepted addresses on the heap.\n struct WSABuf\n end\n end\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 # Winsock's address length is an `int*` where POSIX has a `socklen_t*`:\n # the same width and the same bytes, so the callers above keep one\n # signature.\n def self.__sys_accept(fd : Int32, addr : Pointer(UInt8), addrlen : Pointer(UInt32), family : Int32) : Int32\n def self.__sys_accept_posted(s : UInt64, addr : Pointer(UInt8), addrlen : Pointer(UInt32), family : Int32) : Int32\n def self.__sys_addr_size(family : Int32) : Int32\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 # The failures above arrive as the Winsock number negated.\n def self.__sys_code(result : Int64) : Int32\n def self.__sys_connect(fd : Int32, addr : Pointer(UInt8), family : Int32) : Int32\n # A unix socket connects blocking, and is made non-blocking after:\n # `ConnectEx` is TCP's alone, and the plain `connect` on a socket\n # already non-blocking is not an answer here. Measured on the runner,\n # it came back connected to a path whose listener had closed, and,\n # waited for with `select`, refused a listener that was there. On a\n # socket still blocking it is Go's `connect` on this platform, and\n # its answer is the outcome: the listener is on this machine, and its\n # kernel queues the connection or refuses it at once.\n def self.__sys_connects_blocking?(family : Int32) : ::Bool\n # The plain call, not `__sys_connect`'s `ConnectEx`, which is TCP's: a\n # datagram socket's connect records the peer and returns.\n def self.__sys_dgram_connect(fd : Int32, addr : Pointer(UInt8), family : Int32) : Int32\n # The plain call, not `__sys_send`'s posted one: a datagram of a\n # query's size goes out whole or not at all, and there is no room to\n # wait for.\n def self.__sys_dgram_send(fd : Int32, buf : Pointer(UInt8), len : UInt64) : Int64\n # The resolver's datagram socket: UDP, non-blocking, and - unlike\n # `std/udp`'s - with Windows' report of an ICMP port-unreachable left\n # on. This one is connected to one name server, so the report says\n # that server has nothing on its port, and the next server is asked at\n # once instead of after the timeout.\n def self.__sys_dgram_socket(family : Int32) : Int32\n # Not `await_readable`: the scheduler's readable-wait is a zero-byte\n # overlapped read, and on a datagram socket that read takes the\n # datagram and leaves nothing for the receive after it (measured, and\n # the reason `std/udp` posts its receive instead). `WSAPoll` asks\n # without taking, but it blocks the thread for as long as it is told,\n # and every other task on the thread with it; so it is asked with no\n # wait at all, and the task sleeps on the scheduler between askings -\n # at Windows' timer tick, 15.6 ms, which is nothing next to a name\n # server's round trip.\n def self.__sys_dgram_wait(fd : Int32, ms : Int32) : ::Bool | Cancelled\n # The machine's resolver configuration, read from the registry:\n #\n # - name servers: the TCP/IP service's own `NameServer` (a policy's),\n # then each IPv4 interface that has an address - `NameServer` when\n # it was set by hand, else `DhcpNameServer` - then each IPv6\n # interface's `NameServer`, without repeats, at most\n # `ResolvConf::MAX_NAMESERVERS`;\n # - search list: `SearchList`, else the primary DNS suffix (`Domain`,\n # else `DhcpDomain`).\n #\n # What it cannot see: which interface is up (a stale DHCP lease on an\n # unplugged adapter still has an address in the registry), the order\n # of the interfaces' metrics (they are read in the registry's order),\n # IPv6 servers learned from router advertisements or DHCPv6 (stored as\n # binary, not read), and per-connection suffixes. Those need\n # iphlpapi; see the `lib` above for why it is not linked.\n def self.__sys_dns_config : ResolvConf\n # The failure the last call left, negated. The POSIX arms answer the\n # kernel's errno this way — Linux the negative return itself, darwin\n # `errno` read after a -1 — and this answers Winsock's number in the same\n # shape, so nothing above learns a second convention: a negative result\n # is the failure, and the two predicates below are the only things that\n # read which failure it was.\n def self.__sys_error : Int64\n # The pointer to one of mswsock's extension functions, asked of the\n # socket that will use it. Fetched where it is used rather than cached:\n # it is one call, and both callers are about to do a TCP handshake.\n def self.__sys_extension(s : UInt64, low : UInt64, high : UInt64) : Pointer(UInt64)\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 # A SOCKET from the `Int32` the class carries, and back: see the note\n # above the `lib` block for why that is lossless.\n def self.__sys_handle(fd : Int32) : UInt64\n def self.__sys_in_progress?(result : Int32) : ::Bool\n # Winsock caps a backlog at SOMAXCONN, 200, and turns the next connect\n # away as refused - where Linux holds it - unless the backlog is asked\n # as SOMAXCONN_HINT(n), which is -n: 220 connects to a listener asked\n # for 256 lost 20.\n def self.__sys_listen(fd : Int32, backlog : Int32) : Int32\n # Windows raises no SIGPIPE; a write to a closed connection answers\n # WSAECONNRESET or WSAESHUTDOWN.\n def self.__sys_no_sigpipe(fd : Int32) : Nil\n # The wait a cancelled post still owes the kernel. `CancelIoEx` is a\n # request, and on a socket tied to a completion port the operation is not\n # over when it returns: measured here, the status stays STATUS_PENDING\n # and the address block, the descriptor and the OVERLAPPED are the\n # kernel's until the completion packet is taken *off the port* — the\n # poller's `GetQueuedCompletionStatus` is what writes them. The same\n # measurement on an unassociated socket had the status written by\n # `CancelIoEx` itself, which is why this is not obvious. So\n # `WSAGetOverlappedResult`'s own wait flag cannot be the wait either: it\n # waits on the OVERLAPPED's event, the scheduler's OVERLAPPED has none,\n # and the socket handle it falls back to is never signalled for a socket\n # on a port — it hung forever on the first cancelled receive tried.\n #\n # What ends the operation is the poller, so the frame parks once more and\n # lets it. `IyiScheduler.cancel` has already unregistered this fiber,\n # which is what would have left the packet to be dropped; parking puts it\n # back, and the wake means the dequeue has happened. Asked first, because\n # a completion the poller delivered *before* the cancel is already over\n # and no second packet is coming — parking on one would never wake.\n def self.__sys_overlapped_await(s : UInt64, ov : Pointer(UInt8)) : Int64\n # What a posted operation ended with, read off the operation itself. The\n # completion port's byte count cannot tell a failure from a success that\n # transferred nothing, and a successful `AcceptEx` or `ConnectEx` is\n # exactly that — both complete with zero bytes. Nothing waits here: the\n # completion has already arrived, which is what the 0 says.\n def self.__sys_overlapped_result(s : UInt64, ov : Pointer(UInt8)) : Int64\n # TCP names its protocol; a unix socket has only the one, and says 0.\n def self.__sys_protocol(family : Int32) : Int32\n def self.__sys_recv(fd : Int32, buf : Pointer(UInt8), len : UInt64) : Int64\n def self.__sys_send(fd : Int32, buf : Pointer(UInt8), len : UInt64, ms : Int32) : Int64\n # The send as the wait, with the write's deadline on it: a deadline\n # that comes first cancels the send, and the cancellation's completion\n # wakes this frame, so what the send did is known when it returns.\n def self.__sys_send_posted(s : UInt64, buf : Pointer(UInt8), len : UInt64, ms : Int32) : Int64\n def self.__sys_set_nonblock(fd : Int32) : Int32\n # Not SO_REUSEADDR, which on Windows lets a *different* socket bind a\n # port this one is already bound to, and the last binder takes the\n # packets; what the POSIX arms ask for with it is only the waiver of\n # the TIME_WAIT wait, which Windows does not impose on a listener's\n # port. The opposite: SO_EXCLUSIVEADDRUSE (~SO_REUSEADDR, -5). Without\n # it the port was not the listener's either - a bind to 127.0.0.1:P went\n # through while 0.0.0.0:P listened, and the other way round, from any\n # process, and the more specific listener took the connections.\n def self.__sys_set_reuseaddr(fd : Int32) : Int32\n # IPV6_V6ONLY (27) at IPPROTO_IPV6 (41). Windows' default already, and\n # said anyway: the default is a registry's, and this is the program's.\n def self.__sys_set_v6only(fd : Int32) : Int32\n # SO_ERROR at SOL_SOCKET, as on the POSIX arms. `__sys_connect` above has\n # already waited for the handshake and answered what it ended with, so\n # the shared `connect` reaches this only when its task was cancelled —\n # and `wait_writable` answers `Cancelled` before it gets here. It reads\n # the option rather than guessing, so it is the truth for anything that\n # does ask.\n def self.__sys_sock_error(fd : Int32) : Int32\n def self.__sys_socket(family : Int32) : Int32\n def self.__sys_winsock : Nil\n # WSAEWOULDBLOCK answers both questions here: it is what a non-blocking\n # call with nothing to do says, and it is what a connect that is under\n # way says, where POSIX has EAGAIN and EINPROGRESS for the two.\n def self.__sys_would_block?(result : Int64) : ::Bool\n def accept : IyiSocket | Cancelled | SocketError\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 def closed? : ::Bool\n # `localhost` is 127.0.0.1 and then ::1: a refused or unreachable\n # 127.0.0.1 is tried again at ::1. Windows names both, ::1 first, and a\n # dev server that binds `localhost` there is often on ::1 alone, where\n # a connect to 127.0.0.1 only was \"connection refused\". 127.0.0.1 goes\n # first because a listener here binds `localhost` there. Windows takes\n # 2 s to refuse a connect, so a ::1-only listener is reached in 2 s\n # (2017 ms measured) and a port nobody holds is refused in 4 s, not 2.\n #\n # Any other host name is resolved by the machine's resolver\n # (`Resolver.system`) and its addresses tried in the order the resolver\n # answers them - IPv4 first, then IPv6, as `localhost` is - until one\n # connects; a name that does not resolve is a `SocketError` whose\n # `unresolved?` is true and whose sentence names the host. It panicked,\n # \"cannot resolve address: \\\"example.com\\\"\", for every name but\n # `localhost`, so `HTTP.get(\"http://example.com/\")` could not run.\n #\n # *timeout_ms* bounds each address's handshake (-1: as long as the\n # kernel waits, which on Linux is about two minutes of SYN retries for\n # a host that drops them); one that runs out is a `SocketError` whose\n # `timed_out?` is true, and the next address is tried.\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 # is the caller with a bound; `connect` waits as the kernel does, as it\n # always has.\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 def fd : Int32\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.\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 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 # 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: what\n # `connect` tries, in the order it tries them. See `Resolver#resolve`.\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 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# (the order `connect` tries `localhost` in), each family in the order\n# the hosts file or the server gave it:\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 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.\npub class Resolver\n def attempts : Int32\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 # *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 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 def self.rcode_name(rcode : Int32) : String\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\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.\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 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 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", + "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", + "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" + }, + { + "name": "std/steppable", + "path": "src/std/steppable.iyi", + "sourceSha256": "59ba21cd708b7c394d471ab831bdf485b35a470ecdac4bbbb9d5633fc45f7cb5", + "surface": "module std/steppable\n\n# Implements a step method for iterating from a value.\n# T needs `+`, `-`, `<=>`, and comparison with the integer 0 (`>` / `<`).\npub class NumericStepIterator(T)\n def initialize(current : T, limit : T | ::Nil, step : T, exclusive : ::Bool)\n def next : T | Stop\n def sum(initial : U) : U forall U\nend\n\npub trait Steppable\n # Iterates from self to limit incrementing by step_val on each iteration.\n def step(*, to limit : self | ::Nil = nil, by step_val : self, exclusive : ::Bool = false, & : (self -> ::Nil)) : ::Nil\n # Returns an Iterator that iterates from self to limit by step_val.\n def step(*, to limit : self | ::Nil = nil, by step_val : self, exclusive : ::Bool = false) : NumericStepIterator(self)\nend\n\nimpl Std::Iterator::Iterator for NumericStepIterator(T) forall T\nend\n\nimpl Steppable for Int32\nend\n\nimpl Steppable for Int64\nend\n\nimpl Steppable for Float64\nend\n" + }, + { + "name": "std/string_pool", + "path": "src/std/string_pool.iyi", + "sourceSha256": "82ac6a65e5005b1283ab9ac80a3331935d6c40ec9feb263bd91158f022812f03", + "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", + "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", + "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", + "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", + "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", + "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" + }, + { + "name": "std/text", + "path": "src/std/text.iyi", + "sourceSha256": "e5ac6a08ed71d3404f2d07bdc5ed4dd67e1d9d0a2d29b5ee6056198e73276074", + "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": "de71e76443d3820128318a8a725301c5aa6de7a06df82caf8993794355d43987", + "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 lib LibKernel32\n end\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 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 lib LibKernel32\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 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": "2f58e8374471f2f0d7e5d87a6cd69536dfe8c0af325c7a7b37e04faebac72766", + "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 connection to *host* on *port*, its handshake done and the server's\n # certificate checked for *host*.\n def self.connect(host : String, port : Int32, config : TlsConfig = TlsConfig.new, timeout_ms : Int32 = -1) : TlsClient | TlsError\n def handshake : TlsError | ::Nil\n def initialize(socket : IyiSocket, host : String, config : TlsConfig)\n # The handshake over a connected *socket*, for *host*, done within\n # *timeout_ms* (none when 0).\n def self.over(socket : IyiSocket, host : String, config : TlsConfig = TlsConfig.new, timeout_ms : Int32 = HANDSHAKE_TIMEOUT_MS) : TlsClient | 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 # 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 # 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 def abort : Nil\n # The protocol chosen by ALPN, if one was.\n def alpn_protocol : String | ::Nil\n def cipher_suite : String\n # close_notify, then the socket closed.\n def close : Nil\n def closed? : 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 # A server then reads and drops what the client goes on sending, until\n # it closes, for at most `LINGER_BYTES` and `LINGER_MS` in all: a close\n # with the client's flight unread is a reset, which can take the alert\n # from the client's buffer before it is read - a client refused for its\n # certificate would read \"reset\", not why.\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 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 this is the server's end.\n abstract def server_side? : Bool\n # The version negotiated: \"TLSv1.3\", or \"TLSv1.2\" for a client whose\n # server chose it.\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 def write(data : String | Array(UInt8)) : Int32 | TlsError\n def write_timeout_ms=(ms : Int32 | ::Nil) : Int32 | ::Nil\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)\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 certificate chain, leaf first, and the key that signs for it -\n# Ed25519, ECDSA on P-256 (std/p256) or RSA (std/rsa), each in constant\n# time: what a server presents, and what a client presents when a server\n# asks.\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 and, by RFC 8422\n # 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 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 one as RFC 5915\n # does, or an RSA one), an EC PRIVATE KEY block (SEC 1) or an RSA\n # PRIVATE KEY block (PKCS #1) - or why they are not a certificate and\n # 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, rsa : RsaPrivateKey | ::Nil)\n def leaf : Certificate\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), or for an RSA key\n # rsa_pss_rsae_sha256, -384 and -512 (0x0804 to 0x0806). PKCS #1 v1.5\n # is never one: RFC 8446 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`: what CertificateVerify carries.\n def sign(scheme : Int32, content : Array(UInt8)) : Array(UInt8)\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 # 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 with no PSK, and the \"derived\" salt for the next stage.\n def early_secret : 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 def handshake_secret(shared : Array(UInt8)) : 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 def zeros : Array(UInt8)\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# A connection from a client, its handshake done: TLS 1.3 as a server,\n# its certificate's key (Ed25519 or P-256) signing for it.\npub class TlsServer < TlsConnection\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)\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 def self.over(socket : IyiSocket, config : TlsServerConfig, timeout_ms : Int32 = HANDSHAKE_TIMEOUT_MS) : TlsServer | 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\nend\n\n# What a server presents and asks: its identity, the protocols it speaks\n# by ALPN, and - for mutual TLS - the roots a client's certificate must\n# chain to.\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) while\n # `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) while\n # `client_required`. Not asked when nil.\n def client_roots=(value : Array(Certificate) | ::Nil) : Array(Certificate) | ::Nil\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 : 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 def identity : TlsIdentity\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 def initialize(identity : TlsIdentity)\n def leaf : Certificate\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 def schemes : Array(Int32)\nend\n\n# The versions a client may speak: TLS 1.3, or TLS 1.2 as well (RFC 5246),\n# for a server 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", + "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" + }, + { + "name": "std/traits", + "path": "src/std/traits.iyi", + "sourceSha256": "0d99d815071b1aa2725d090eec449cebbc4e3c832598e6426f5347a75b4025bf", + "surface": "module std/traits\n\npub trait Comparable\n def <(other : self) : Bool\n def <=(other : self) : Bool\n abstract def <=>(other : self) : Int32\n def ==(other : self) : Bool\n def >(other : self) : Bool\n def >=(other : self) : Bool\n def between?(min : self, max : self) : Bool\n def clamp(min : self, max : self) : self\n def clamp(range : Range) : self\nend\n\npub trait Hashable\n abstract def hash_key : Int32\nend\n\npub trait Num : ::Std::Traits::Comparable\n abstract def add(other : self) : self\n abstract def mul(other : self) : self\n abstract def self.one : self\n # Class-level requirements. `Enumerable#sum` and `#product` need an additive\n # and a multiplicative identity, and an identity belongs to the type, not to\n # any value of it — there is no element to ask when the collection is empty.\n abstract def self.zero : self\nend\n\nimpl Comparable for Int32\n def <=>(other : self) : Int32\nend\n\nimpl Num for Int32\n def self.one : self\n def add(other : self) : self\n def mul(other : self) : self\nend\n\nimpl Hashable for Int32\n def hash_key : Int32\nend\n\nimpl Comparable for Int64\n def <=>(other : self) : Int32\nend\n\nimpl Num for Int64\n def self.one : self\n def add(other : self) : self\n def mul(other : self) : self\nend\n\nimpl Hashable for Int64\n def hash_key : Int32\nend\n\nimpl Comparable for UInt8\n def <=>(other : self) : Int32\nend\n\nimpl Num for UInt8\n def self.one : self\n def add(other : self) : self\n def mul(other : self) : self\nend\n\nimpl Hashable for UInt8\n def hash_key : Int32\nend\n\nimpl Comparable for UInt64\n def <=>(other : self) : Int32\nend\n\nimpl Num for UInt64\n def self.one : self\n def add(other : self) : self\n def mul(other : self) : self\nend\n\nimpl Hashable for UInt64\n def hash_key : Int32\nend\n\nimpl Comparable for Float64\n def <=>(other : self) : Int32\nend\n\nimpl Num for Float64\n def self.one : self\n def add(other : self) : self\n def mul(other : self) : self\nend\n\nimpl Hashable for Float64\n def hash_key : Int32\nend\n\nimpl Comparable for String\n # `<=>` is not a method the prelude's `String` has, so this was a call\n # nobody could make; `<` and `==` are what it does have.\n def <=>(other : self) : Int32\nend\n\nimpl Hashable for String\n def hash_key : Int32\nend\n\nimpl Comparable for Char\n def <=>(other : self) : Int32\nend\n\nimpl Hashable for Char\n def hash_key : Int32\nend\n\nimpl Hashable for Bool\n def hash_key : Int32\nend\n\nimpl Comparable for Int8\n def <=>(other : self) : Int32\nend\n\nimpl Num for Int8\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 Hashable for Int8\n def hash_key : Int32\nend\n\nimpl Comparable for Int16\n def <=>(other : self) : Int32\nend\n\nimpl Num for Int16\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 Hashable for Int16\n def hash_key : Int32\nend\n\nimpl Comparable for UInt16\n def <=>(other : self) : Int32\nend\n\nimpl Num for UInt16\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 Hashable for UInt16\n def hash_key : Int32\nend\n\nimpl Comparable for UInt32\n def <=>(other : self) : Int32\nend\n\nimpl Num for UInt32\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 Hashable for UInt32\n def hash_key : Int32\nend\n\nimpl Comparable for Int128\n def <=>(other : self) : Int32\nend\n\nimpl Num for Int128\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 Hashable for Int128\n def hash_key : Int32\nend\n\nimpl Comparable for UInt128\n def <=>(other : self) : Int32\nend\n\nimpl Num for UInt128\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 Hashable for UInt128\n def hash_key : Int32\nend\n\nimpl Comparable for Float32\n def <=>(other : self) : Int32\nend\n\nimpl Num for Float32\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 Hashable for Float32\n def hash_key : Int32\nend\n\nimpl Hashable for Tuple(*T) forall T\n def hash_key : Int32\nend\n\nimpl Hashable for NamedTuple(T) forall T\n def hash_key : Int32\nend\n\nimpl Hashable for Array(T) forall T\n def hash_key : Int32\nend\n\nimpl Hashable for Nil\n def hash_key : Int32\nend\n\nimpl Hashable for Symbol\n def hash_key : Int32\nend\n" + }, + { + "name": "std/tuple", + "path": "src/std/tuple.iyi", + "sourceSha256": "5a67030cf1aec1ddb1dd137df717cdb0d44df9e0edd3714ab92754c6a5a86e1a", + "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": "fe3d3e40e1586bbf90ce1a0db865944f9467e4499abcc8b3a6a392c3d5419ae0", + "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 lib LibWs2_32\n struct PollFd\n end\n # Both are kept on the caller's frame rather than the heap: the kernel\n # reads and writes them after a post, while the fiber is parked, and a\n # heap chunk whose only reference is a local the posting call has\n # finished with is a chunk the collector can hand to somebody else\n # first.\n struct WSABuf\n end\n end\n alias SockLen = Int32\n def self.__is_eagain(res : Int64) : ::Bool\n # Never: Winsock has no EINTR. A call either answers, or answers\n # WSAEWOULDBLOCK, or is posted and completed.\n def self.__is_eintr(res : Int64) : ::Bool\n # A datagram that did not fit is an error here where it is a silent\n # truncation everywhere else: WSAEMSGSIZE (10040), with the buffer\n # already filled, which is why the caller answers its capacity.\n def self.__is_msgsize(res : Int64) : ::Bool\n # Never: the report is turned off in `__sys_socket`.\n def self.__is_refused(res : Int64) : ::Bool\n # WSAEINVAL (10022): Winsock's answer to a receive, or an address asked,\n # on a socket that has none yet - one `client` made and nothing sent\n # from. POSIX has nothing queued there and port 0; Windows panicked.\n def self.__is_unbound(res : Int64) : ::Bool\n def self.__sys_bind(fd : Int32, addr : Pointer(UInt8), len : Int32) : Int32\n # A datagram socket's `connect` only records the peer, so there is no\n # handshake to wait for and nothing to post.\n def self.__sys_connect(fd : Int32, addr : Pointer(UInt8), len : Int32) : Int32\n # Winsock's error number, negated, which is the shape the POSIX arms\n # answer the kernel's errno in; the two predicates at the bottom are the\n # only things that read which number it was.\n def self.__sys_error : Int64\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_handle(fd : Int32) : UInt64\n # The wait a cancelled post still owes the kernel. `CancelIoEx` is a\n # request, and on a socket tied to a completion port the operation is not\n # over when it returns: measured here, the status stays STATUS_PENDING\n # and the descriptor, the caller's address and the OVERLAPPED are the\n # kernel's until the completion packet is taken *off the port* — the\n # poller's `GetQueuedCompletionStatus` is what writes them. The same\n # measurement on an unassociated socket had the status written by\n # `CancelIoEx` itself, which is why this is not obvious. So\n # `WSAGetOverlappedResult`'s own wait flag cannot be the wait either: it\n # waits on the OVERLAPPED's event, the scheduler's OVERLAPPED has none,\n # and the socket handle it falls back to is never signalled for a socket\n # on a port — it hung forever on the first cancelled receive tried.\n #\n # What ends the operation is the poller, so the frame parks once more and\n # lets it. `IyiScheduler.cancel` has already unregistered this fiber,\n # which is what would have left the packet to be dropped; parking puts it\n # back, and the wake means the dequeue has happened. Asked first, because\n # a completion the poller delivered *before* the cancel is already over\n # and no second packet is coming — parking on one would never wake.\n def self.__sys_overlapped_await(s : UInt64, ov : Pointer(UInt8)) : Int64\n # What the posted operation ended with, read off the operation itself:\n # the completion port's byte count cannot tell a failure from a datagram\n # of no bytes, which is a legal datagram. Nothing waits here, which is\n # what the 0 says.\n def self.__sys_overlapped_result(s : UInt64, ov : Pointer(UInt8)) : Int64\n # One descriptor, one event, and a bound on the wait, which is what the\n # epoll and kqueue arms do with theirs.\n def self.__sys_poll_read(fd : Int32, timeout_ms : Int32) : ::Bool\n # Winsock's address length is an `int*` where POSIX has a `socklen_t*`:\n # the same width and the same bytes, so the callers keep one signature.\n def self.__sys_recvfrom(fd : Int32, buf : Pointer(UInt8), len : UInt64, flags : Int32, addr : Pointer(UInt8), addrlen : Pointer(UInt32)) : Int64\n # The receive that waits, which on Windows is a posted one: see the note\n # in `receive_into` for why the readable-wait cannot be used here. The\n # buffer descriptor and the flags stay on this frame for the whole\n # operation, and the address and its length are the caller's, which\n # `receive_from` reads after this returns.\n def self.__sys_recvfrom_posted(fd : Int32, buf : Pointer(UInt8), capacity : Int32, addr : Pointer(UInt8), addrlen : Pointer(UInt32)) : Int32 | Cancelled\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 : Int32) : 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", + "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/uri", + "path": "src/std/uri.iyi", + "sourceSha256": "d8415c047bdcf59561a5bd1a38e4a8ac058dc0519d27c38b3f92e39b4493d7ba", + "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", + "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", + "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", + "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", + "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": "5bae4254f0d2d9b117c7bb5a89aee875693b1cb59a2760a005979f19e46de508", + "surface": "module std/x509\n\npub struct Certificate\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 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), 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 def not_after : Time\n def not_before : Time\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 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 # 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\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 # 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\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# 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", + "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", + "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", + "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", + "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", + "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" + } + ], + "prelude": { + "sources": [ + { + "path": "src/iyi/array.iyi", + "sha256": "743e8683780994c0c1c3e7bb927a8d178a77e94c4261c049a6ff976b2f0de894" + }, + { + "path": "src/iyi/atomic.iyi", + "sha256": "4ab1697b993ac0b43bb3d58b8e9be9e95fa2cea1900830a39626d9289cda370e" + }, + { + "path": "src/iyi/concurrency.iyi", + "sha256": "377eeca7c46b21722720bf7c04f19bfd579fd596b3e3284295c3f29289991546" + }, + { + "path": "src/iyi/enum.iyi", + "sha256": "50ffac2c913d9b9dd29ee3f33c9ae3958fcdac3e1ef593f6fbfc06074bb7477c" + }, + { + "path": "src/iyi/file.iyi", + "sha256": "431b8bf5a51054074315815806a66caf64c25ddc4b6b55e06dd53511f18f500b" + }, + { + "path": "src/iyi/float.iyi", + "sha256": "9d80115e42794ed62904bdd85a60874f3b94f46c69ec585492b86e0f7e410006" + }, + { + "path": "src/iyi/hash.iyi", + "sha256": "580ef1c3bc63419b8df4724b685a551166f02072a4e7b8fdbc0efab34de30167" + }, + { + "path": "src/iyi/io.iyi", + "sha256": "268eeea54810c6949118bd362e108977e95c0c5021845e626d75d1d579617b9e" + }, + { + "path": "src/iyi/macros.iyi", + "sha256": "8c44fe01776ca3a963f2d2fc862c29afdcd3029683fb25c75d6ff460d646a00f" + }, + { + "path": "src/iyi/number.iyi", + "sha256": "4670aae20350cb7f1961bf7b4354d1824b76595598a855b50eb76e170874c24f" + }, + { + "path": "src/iyi/object.iyi", + "sha256": "f08851e354c3c5c3a33cde728f2dbb75d5159366b679e1e2bfbc748524481f14" + }, + { + "path": "src/iyi/prelude.iyi", + "sha256": "cd5f17aa7ef81c6700acf40039efd7c6197b33da8a67889bf63a50fc3f674cea" + }, + { + "path": "src/iyi/primitives.iyi", + "sha256": "3a47aad1995bba3bfd5b3f48db8fa6aaf92000e6caae19be58d4d4ecf1691a48" + }, + { + "path": "src/iyi/range.iyi", + "sha256": "c99a928a87a1e72ad2dc48a9505a37afcaf01e178ee01f38043a17dc8f7cd3ed" + }, + { + "path": "src/iyi/set.iyi", + "sha256": "8b53d648fe60b2f5ebf144a3ac4d8d929ed5f062df6955f7d92c09a8a10c83a9" + }, + { + "path": "src/iyi/string.iyi", + "sha256": "7c32dd645bdadfd7a49342fcc783ec65ab0bbff9db9dae7d12b13d23f3c5a02a" + }, + { + "path": "src/iyi/testing.iyi", + "sha256": "f35cc2a0bcd30498a24e67b68b7a8652581c3e1dbc0c4311206674f8a447b15f" + }, + { + "path": "src/iyi/thread.iyi", + "sha256": "0b9b2073a96b7d963182fe8b2c2d778dcb3d49b5fb119a695ff1962d35ac6e31" + } + ], + "types": [ + { + "name": "Array", + "surface": "# The one sequence — SPEC.md, \"0.1.0\", item 3.\n#\n# `Array` is a compiler-known generic class, like `String`: an array literal is\n# lowered to `Array(T).unsafe_build` and `to_unsafe` before any of this is\n# read (`LiteralExpander#expand`), so those two names are not ours to choose.\n#\n# A buffer, a size and a capacity. Growth doubles, which is the only policy\n# that keeps a run of `<<` linear, and there is no shrinking; `shift` steps\n# the buffer forward, so the capacity is what is left past it.\nclass Array(T)\n\n # The elements in both, each once, in this array's order. Taking a value\n # out of the table as it is kept is what keeps it once.\n def &(other : Array(T)) : Array(T)\n\n # This array *times* times over, one after the other: `[1, 2] * 2` is\n # `[1, 2, 1, 2]`, and zero times is empty.\n def *(times : Int32) : Array(T)\n\n # The two in order, as a new array; neither operand changes.\n def +(other : Array(T)) : Array(T)\n\n # The elements not in *other*, duplicates and order kept: `[1, 1, 2, 3] -\n # [2]` is `[1, 1, 3]`. Equality is `==`, asked of a table when *other* is\n # long enough for a search of it per element to cost more than building\n # one, as the other library decides at sixteen.\n def -(other : Array(T)) : Array(T)\n\n def <<(value : T) : Array(T)\n\n def ==(other : Array(T)) : Bool\n\n # `a[1, 2]`: *count* elements from *start*, or as many as there are. A\n # start that wraps below zero or lies past the end is refused, as an index\n # is; a start of exactly `size` is the empty tail, `[]`, as in the other\n # library, because the position after the last is where an append goes.\n def [](start : Int32, count : Int32) : Array(T)\n\n # A negative index counts from the end, so `a[-1]` is the last element.\n # Still out of range after that wrap (or empty) raises, same message.\n def [](index : Int32) : T\n\n # `a[1..2]`, `a[1...-1]`, `a[2..]`, `a[..1]`: either end may be negative,\n # and an end past the last element stops at it. The start is held to what\n # `[](start, count)` holds it to, so `[1, 2, 3][3..]` is `[]` and\n # `[1, 2, 3][4..]` is refused: a range that begins outside the array asks\n # about elements that are not there.\n def [](range : Range(B, E)) : Array(T) forall B, E\n\n def []=(start : Int32, count : Int32, values : Array(T)) : Array(T)\n\n # `a[1, 2] = x` puts one element where the span was, and `a[1, 2] = [x,\n # y, z]` puts all of them there: the array grows or shrinks by the\n # difference, as the other library's does. A start of `size` appends.\n def []=(start : Int32, count : Int32, value : T) : T\n\n def []=(index : Int32, value : T) : T\n\n def []=(range : Range(B, E), values : Array(T)) : Array(T) forall B, E\n\n def []=(range : Range(B, E), value : T) : T forall B, E\n\n # The same span, or nil where `[]` would refuse the start. A negative\n # count is a mistake in the call rather than an answer about this array,\n # and is refused by both.\n def []?(start : Int32, count : Int32) : Array(T) | ::Nil\n\n def []?(index : Int32) : T | ::Nil\n\n def []?(range : Range(B, E)) : Array(T) | ::Nil forall B, E\n\n def all?(& : (T -> Bool)) : Bool\n\n def any?(& : (T -> Bool)) : Bool\n\n # The elements that are not nil, in an array whose type says so: what a\n # list of `to_i?` answers becomes once the failures are dropped.\n def compact\n\n # `a.concat(a)` is a doubling, not a loop: `each` walked *other*'s growing\n # size while `<<` grew it, so `[1, 2].concat(itself)` grew the buffer until\n # the capacity arithmetic overflowed. The length is read once.\n def concat(other : Array(T)) : Array(T)\n\n # The questions a list gets asked with one answer. `find` answers nil\n # when none does, the way Crystal's does and `index` below already did.\n def count(& : (T -> Bool)) : Int32\n\n # Every element equal to `value` out, answering the last or nil, as the\n # other library's: it took the first alone, `[1, 2, 1]` left `[2, 1]`.\n def delete(value : T) : T | ::Nil\n\n def dup : Array(T)\n\n def each(& : (T -> Nil)) : Nil\n\n def each_with_index(& : (T, Int32 -> Nil)) : Nil\n\n def empty? : Bool\n\n def find(& : (T -> Bool)) : T | ::Nil\n\n # The first `n`, or all of them when there are fewer: a prefix, not a\n # question, so it never panics.\n def first(n : Int32) : Array(T)\n\n # `first` on an empty array is a mistake rather than a nil: the array knows\n # it is empty and so does whoever wrote the call. `last?` above answers the\n # other question, and is the one the trait defaults use.\n def first : T\n\n # And the nilable question beside it, as `last?` is beside `last`: without\n # an import a program had `last?` and not `first?`.\n def first? : T | ::Nil\n\n # Every element that is not an array, from every array nested inside at\n # any depth, in the order they are written: `[[1, [2]], 3]` is `[1, 2,\n # 3]`. The element type is computed the same way, through every level, so\n # `[[1], [[2]]].flatten` is an `Array(Int32)`.\n def flatten\n\n # Hashing follows `==`, mixed as a tuple's members are (object.iyi): an\n # array writes `==`, so `Reference#hash` answered its type's id and every\n # array key shared one slot - 40,000 of them took 1.2 s to insert.\n def hash : Int32\n\n def includes?(value : T) : Bool\n\n # Where a value first is, or nil.\n def index(value : T) : Int32 | ::Nil\n\n def inspect : String\n\n def join(separator : String) : String\n\n # Here rather than on `Enumerable`: the trait is iyi's, written in\n # `src/std`, and every one of its methods that returns a sequence\n # returns one of these. So an `Array` is what a sample ends up holding, and\n # `join` is what it ends up calling.\n # With nothing between: `[\"a\", \"b\"].join` is `\"ab\"`.\n def join : String\n\n # The last `n`, in their order, or all of them when there are fewer.\n def last(n : Int32) : Array(T)\n\n # `last` on an empty array is a mistake rather than a nil, as `first` is.\n def last : T\n\n def last? : T | ::Nil\n\n def map(& : (T -> U)) : Array(U) forall U\n\n # The element whose key is largest, or smallest, the first of equals; a\n # NaN key is above every other, as `<=>` places it. Empty is a mistake.\n def max_by(& : (T -> U)) : T forall U\n\n def min_by(& : (T -> U)) : T forall U\n\n def new(capacity : Int32)\n\n def new\n\n # Removes and returns the last element. Empty is a mistake, as with `last`.\n def pop : T\n\n # The last element or nil, the stack's twin of `shift?`: `while item =\n # stack.pop?` empties it from the top.\n def pop? : T | ::Nil\n\n # `<<` by the other library's name, and with several values at once: the\n # room for all of them is made in one step.\n def push(value : T) : Array(T)\n\n def push(*values : T) : Array(T)\n\n def reduce(initial : U, & : (U, T -> U)) : U forall U\n\n def reject(& : (T -> Bool)) : Array(T)\n\n # A copy in the other order.\n def reversed : Array(T)\n\n def select(& : (T -> Bool)) : Array(T)\n\n # Removes and returns the first element. The buffer steps past it rather\n # than the rest moving down: moving them made draining a queue quadratic,\n # and `while item = queue.shift?` - how the prelude says a queue is\n # drained - took 1.6 s over 100,000 items. The slot stepped past is still\n # the buffer's chunk, which the collector keeps by the pointer into it,\n # and the next growth copies only what is left.\n def shift : T\n\n # The first element or nil, which is how a queue is drained: `while\n # item = queue.shift?`.\n def shift? : T | ::Nil\n\n def size : Int32\n\n def sort_in_place(& : (T, T -> Int32)) : self\n\n # The pair for elements that already compare, by `<`. SPEC.md III.1.7a\n # pairs `sort_in_place` with `sorted` and the hint for `sort!` names both,\n # but only `sorted` had this form: \"expected to be invoked with a block\".\n def sort_in_place : self\n\n # Sorted on a key each element answers, ascending by `<`: the same pair\n # as above, one name longer. Each element's key is computed once, and the\n # pairs of key and element are what the merge sort orders.\n def sort_in_place_by(& : (T -> U)) : self forall U\n\n # The copy, with the comparison written out. One body, because the difference\n # between the pair is which array it runs on. The block is handed on through\n # `yield`, not captured: a captured block is kept to call later, and `!` in\n # it was refused as \"maybe by a task\" although the sort runs it here.\n def sorted(& : (T, T -> Int32)) : Array(T)\n\n def sorted : Array(T)\n\n def sorted_by(& : (T -> U)) : Array(T) forall U\n\n # The sum of the elements, and `T.zero` (src/iyi/number.iyi) when there\n # are none, as `std/enumerable`'s `sum` answers: this one raised \"sum of\n # an empty array\" where `Set(Int32).new.sum` was 0. `[\"a\", \"b\"].sum` is a\n # compile error, as in the other library; `join` puts strings together.\n def sum : T\n\n # The sum of what the block answers for each element, and the block's\n # own zero when there are none.\n def sum(& : (T -> U)) : U forall U\n\n # `[1, 2, 3]`, the way it is written: an array printed is its elements\n # inspected, so a string inside keeps its quotes and a nil says so. Cast,\n # or `Tuple.new.to_a`'s `Array(NoReturn)` \"can't infer block return type\".\n def to_s : String\n\n # The same values as a `Set`, in the order first met.\n def to_set : Set(T)\n\n def to_unsafe : Pointer(T)\n\n # Rows into columns: `[[1, 2], [3, 4]].transpose` is `[[1, 3], [2, 4]]`.\n # Rows of different lengths have no columns to give, and are refused with\n # the first row that differs.\n def transpose\n\n # Keeps only the span, in place, and answers this array: `truncate(1, 2)`\n # leaves the second and third, refusing a start `[](start, count)` refuses.\n def truncate(start : Int32, count : Int32) : Array(T)\n\n def truncate(range : Range(B, E)) : Array(T) forall B, E\n\n # Each value once, in the order first met, found through a table: a\n # search of what was kept so far made it quadratic, 284 ms for 200,000\n # elements with 10,000 distinct.\n def uniq : Array(T)\n\n # Each element whose key is the first of its kind, in the order met: the\n # key is the block's, so `words.uniq(&.downcase)` keeps one spelling.\n def uniq(& : (T -> U)) : Array(T) forall U\n\n # :nodoc:\n def unsafe_set_size(size : Int32) : Nil\n\n # In front, in the order written: `[3].unshift(1, 2)` is `[1, 2, 3]`.\n # Every element moves up to make the room, so a queue that grows at the\n # front is quadratic where one that grows at the back and drains with\n # `shift` is not.\n def unshift(value : T) : Array(T)\n\n def unshift(*values : T) : Array(T)\n\n # Pairs, element by element, as many as this array holds: a shorter\n # *other* is refused at its first missing index, as the other library does.\n def zip(other : Array(U)) : Array(Tuple(T, U)) forall U\n\n # The same pairs handed to a block, with no array of them made: the\n # other library's `a.zip(b) { |x, y| ... }`, which a training loop calls\n # a few million times, and which refuses a shorter *other* the same way.\n def zip(other : Array(U), & : (T, U -> _)) : Nil forall U\n\n # The elements in either, each once, this array's first: `[1, 2] | [2,\n # \"a\"]` is `[1, 2, \"a\"]`, whose element type is both arrays'.\n def |(other : Array(U)) : Array(T | U) forall U\n\n # `Array.new(729, 0)`: *size* copies of *value*, the other library's fill\n # form. A second argument used to be an arity error beside a one-argument\n # form that means capacity, which a port reads as the fill it was.\n def self.new(size : Int32, value : T) : Array(T)\n\n # `Array(T).new(n) { |i| ... }`: n elements, each the block's answer for\n # its index - a row of a grid, a table of squares.\n def self.new(size : Int32, & : (Int32 -> T)) : Array(T)\n\n # An array of *capacity* elements that are not written yet. The literal\n # expander fills them through `to_unsafe` immediately afterwards, which is\n # why this is allowed to hand out a size it has not stored anything at.\n def self.unsafe_build(capacity : Int32) : Array(T)\nend\n" + }, + { + "name": "Atomic", + "surface": "# iyi: `Atomic(T)` — a word two threads may both write. SPEC.md III.4.10.\n#\n# The first thing in this prelude that assumes a second thread, and it\n# arrived the way everything here arrives, with a caller: the thread\n# floor (`bench/thread_floor.iyi`) counted its threads in and out on\n# three `fun`s of inline asm, and the release build's `xchg` clobbered a\n# register the asm had declared an input, which cost a day. These six\n# verbs are the compiler's own instructions instead — `atomicrmw`,\n# `cmpxchg`, an ordered load and an ordered store — so the register\n# allocation is LLVM's problem, the aarch64 arm is whatever the target's\n# CPU offers (`ldaxr`/`stlxr` loops, or LSE's `ldaddal` where the\n# machine has it), and nothing is spelled twice per architecture. The\n# floor holds: `nm -u` on a program that uses one names nothing new,\n# because a 64-bit-or-narrower atomic is an instruction on every target\n# iyi links for, never a `__atomic_*` call. `bench/thread_floor.sh`\n# asserts that, plain and release, on both Linux arms and on darwin.\n#\n# **One ordering, sequentially consistent, and no parameter to weaken\n# it.** This is Go's rule (its memory model makes every `sync/atomic`\n# operation sequentially consistent and the package has no ordering\n# argument) rather than C++'s or Rust's, and it is a decision, not an\n# omission. An ordering parameter is the part of an atomic API people\n# get wrong, the compiler cannot check, and a program cannot test for;\n# the cost of not having it is a fence on the architectures that\n# distinguish the orderings, and on x86_64 — the machine the floor was\n# measured on — a sequentially consistent `add` and a relaxed one are\n# the same `lock xadd`. On aarch64 the difference is `ldaddal` against\n# `ldadd`, and it is not yet a number: the day a profile of the marker\n# or the scheduler names one, that measurement is what adds the\n# weaker spelling, and it will be added as its own verb rather than a\n# parameter, so a call site says what it means.\n#\n# `T` is one of the four integers `primitives.iyi` gives arithmetic to.\n# A pointer or a reference is refused by name rather than admitted\n# untested: the arena list that will want one is the caller that will\n# bring it. The verbs and their answers are Crystal's, so a program that\n# reads the same under `--crystal` means the same: `add`, `sub` and\n# `swap` answer the value the word held before; `compare_and_set`\n# answers that value and whether the exchange happened, which is what a\n# retry loop needs without a second load.\n#\n# An `Atomic(T)` is a struct holding the word, so it lives where it is\n# declared — a class's field, a class variable, a mapping — and the\n# instructions address that word in place. Copying one copies a number,\n# not a synchronisation, which is the same contract Crystal's has.\nstruct Atomic(T)\n\n # `word += value`; answers what the word held before.\n def add(value : T) : T\n\n # If the word holds `expected`, it now holds `desired`. Answers what the\n # word held, and whether the exchange happened; a retry loop feeds the\n # first back in as its next `expected`.\n def compare_and_set(expected : T, desired : T) : Tuple(T, Bool)\n\n # The word, read once, ordered.\n def get : T\n\n def new(value : T)\n\n # The word, written once, ordered.\n def set(value : T) : Nil\n\n # `word -= value`; answers what the word held before.\n def sub(value : T) : T\n\n # `word = value`; answers what the word held before.\n def swap(value : T) : T\nend\n" + }, + { + "name": "Bool", + "surface": "# `true` and `false`.\nstruct Bool < Value\n\n def !=(other : Bool) : Bool\n\n def ==(other : Bool) : Bool\n\n def to_s : String\nend\n" + }, + { + "name": "Cancelled", + "surface": "# What a wait answers when its task was cancelled (III.4.2): an error\n# member. Here, not in the runtime: `IyiSocket` answers it on every target.\nstruct Cancelled\n\n def message : String\n\n def new\nend\n" + }, + { + "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# `T : Share` is not enforced yet for the reason the file header gives:\n# one thread cannot race, and a marker that gates nothing cannot be\n# tested for gating. `select` is answered by this protocol — a node per\n# arm, one nonce, losers stale by arithmetic until the woken select takes\n# 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`).\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 # Assumes `send_ready?`. A rendezvous puts the value in the parked\n # receiver's own slot (`hand`). It went into the buffer, and the send\n # answered delivered on the word of the wake: a select woken that way\n # took another arm ready by then, a receiver cancelled before it ran\n # answered `Cancelled`, and either way the value stayed in a channel of\n # no capacity after its sender left. A buffered send completes when the\n # buffer takes it, so there the woken receiver finds it in the buffer.\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 # Nothing happens on the arm `take_now` 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 def send_select_action(value : T) : IyiSendSelect(T)\n\n # Assumes `receive_ready?`: the buffer, then a parked sender's box,\n # then the refusal.\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 def self.select(actions : U, bound : Int32) forall U\nend\n" + }, + { + "name": "ChannelClosed", + "surface": "struct ChannelClosed\n\n def message : String\n\n def new\nend\n" + }, + { + "name": "Char", + "surface": "# One character - a byte, in this prelude, which reads its strings as ASCII.\nstruct Char < Value\n\n def !=(other : Char) : Bool\n\n # The character *other* code points on: what `'a'..'z'` steps by. `chr`\n # refuses a surrogate too, where `'\\u{D7FF}' + 1` was U+D800.\n def +(other : Int32) : Char\n\n def <(other : Char) : Bool\n\n def <=(other : Char) : Bool\n\n def ==(other : Char) : Bool\n\n def >(other : Char) : Bool\n\n def >=(other : Char) : Bool\n\n def alphanumeric? : Bool\n\n def bytesize : Int32\n\n def digit? : Bool\n\n # `0x10FFFF` is the last code point there is; past it a `'?'` goes out\n # rather than a sequence no reader can decode. `String::Builder` writes\n # these too, which had its own copy of the four arms.\n def each_byte(& : (UInt8 -> Nil)) : Nil\n\n def hash : Int32\n\n def inspect : String\n\n # Itself as `inspect` writes it between *quote*s, the other library's\n # escapes; not `printable?`, as `\\u` and hex. `'\\''` was `'''`.\n def inspect_into(io : String::Builder, quote : Char) : String::Builder\n\n # The classes a word counter asks about, ASCII's: a letter is one of the\n # fifty-two, a digit one of the ten. Whitespace is the other library's:\n # the space and `\\t` to `\\r`, and above ASCII Unicode's Zs, Zl and Zp, so\n # U+00A0 and U+3000 are and U+0085, a control, is not. It was ASCII's.\n def letter? : Bool\n\n def ord : Int32\n\n # Not a control (Cc), format (Cf) or private-use (Co) character of\n # Unicode 15, nor whitespace but the space: the other library's.\n def printable? : Bool\n\n def to_s : String\n\n def whitespace? : Bool\nend\n" + }, + { + "name": "Class", + "surface": "metaclass Class < Value\n\n # The same type, whatever static type the class value has: `Sub ==\n # Sub.as(Base.class)` is true. A class value is its metaclass's id.\n def ==(other : Class) : Bool\n\n # The same type, whatever static type the class value has: `Sub ==\n # Sub.as(Base.class)` is true. A class value is its metaclass's id.\n def ==(other : Class) : Bool\n\n # `Int32 === 1`, and `Int32 | String` as a value: a `when` asks these of\n # the types in a tuple of types and of a union it is written with.\n def ===(other) : Bool\n\n # `Int32 === 1`, and `Int32 | String` as a value: a `when` asks these of\n # the types in a tuple of types and of a union it is written with.\n def ===(other) : Bool\n\n def self.|(other : U.class) forall U\n\n def self.|(other : U.class) forall U\nend\n" + }, + { + "name": "Enum", + "surface": "# What an `enum` can do — SPEC.md, \"0.1.0\", item 3.\n#\n# The compiler gives an enum exactly two methods: `value`, the integer the\n# member was numbered with, and `new`, which wraps one (`EnumType#initialize`).\n# Everything a program does with an enum is the library's, and until\n# `samples/iyi/enums.iyi` asked there was none of it — so the answers were\n# `Object`'s: `Level::Warn == Level::Warn` was **false**, `to_s` printed the\n# type's name, and `case level when Level::Warn` matched nothing, because a\n# `when` naming a member lowers to `Level::Warn === level` and `===` is `==`.\n# The compiler's own per-member question methods (`level.warn?`) call that\n# same `==`, and a `@[Flags]` enum's call `includes?`, so those were broken by\n# one gap apiece.\n#\n# `Enum` is predefined — it is what every enum's ancestor is, created before a\n# prelude line is read — so this reopens it the way `Range` and `String` are\n# reopened rather than declaring it.\n#\n# Read off the type at each instantiation: a method here is stencilled onto the\n# enum that calls it, so `@type` is that enum and `@type.constants` are its\n# members. Nothing walks a table at run time and nothing is allocated to answer\n# a name: `to_s` is a `case` over integers the compiler folds.\nstruct Enum < Value\n\n def &(other : self) : self\n\n # An order, because a level is a threshold: `level >= Level::Info` is the\n # first thing a program writes after a `case`. Four operators rather than a\n # `Comparable` include — the prelude has no such module, and a trait would be\n # `std/traits`' to carry (SPEC.md II.6).\n def <(other : self) : Bool\n\n def <=(other : self) : Bool\n\n # Equality is the value's, which is what makes every other name work: `!=`\n # and `===` are `Object`'s and both read this, `case` lowers to `===`, and\n # the question methods the compiler writes are `self == Member`.\n def ==(other : self) : Bool\n\n def >(other : self) : Bool\n\n def >=(other : self) : Bool\n\n # A key in a `Hash` is hashed and then compared, so a type used as one owes\n # both (`src/iyi/hash.iyi`). The value's own hash: `to_i32` panicked past it.\n def hash : Int32\n\n # Whether every bit of *other* is in this one, which is what a `@[Flags]`\n # enum's question methods ask: the compiler writes `mode.read?` as\n # `self.includes?(Mode::Read)`.\n def includes?(other : self) : Bool\n\n # `#{Level::Warn.inspect}` is the member written down rather than printed,\n # and for an enum the two are the same text. It is here because `Object`'s\n # sends it back to `to_s` — which was the type's name — and a sample that\n # inspects a `Level?` was reading that.\n def inspect : String\n\n # The member's name, and the value's digits where no member has it —\n # `Level.new(9).to_s` is `\"9\"`, which is what a value off a wire prints as.\n #\n # A flags enum answers with a member's name when one has its value (`None`\n # for zero, `All` for every bit, which was skipped), else the names of the\n # bits it holds, each named once, and the rest as a number: `Read | 8`.\n def to_s : String\n\n # The two set operators a flags enum is built to be used with. `Mode::Read |\n # Mode::Write` is how one is written down, and `mode & Mode::Write` is how a\n # bit is taken out of it.\n def |(other : self) : self\n\n # A member from the integer it was numbered with, or `nil` where no member\n # has it. `new` takes any value of the base type by design — that is how an\n # enum comes back from C or off a wire — and this is the question that asks\n # whether the one in hand is a member.\n def self.from_value?(value : Int) : self | ::Nil\n\n # The same list said in text, which is what a message naming the choices\n # needs: `\"one of #{Level.names.join(\", \")}\"`.\n def self.names : Array(String)\n\n # A member read back from text, or `nil` where no member has that name. What\n # a program does with an argument or a configuration line, so the comparison\n # is the lenient one a person types: `\"warn\"`, `\"Warn\"` and `\"WARN\"` all\n # answer `Level::Warn`, and nothing else does.\n #\n # `nil` rather than a panic, because the text is somebody else's input and\n # the caller is the only thing that knows what to say about it (III.1).\n def self.parse?(name : String) : self | ::Nil\n\n # Every member, in the order they were written. A menu, a validation table,\n # a loop that has to reach all of them — and for a flags enum the members\n # rather than `None` and `All`, which are the compiler's own.\n def self.values : Array(self)\nend\n" + }, + { + "name": "File", + "surface": "class File\n\n def self.delete(path : String) : Nil\n\n def self.exists?(path : String) : Bool\n\n # Crystal's mode strings: \"r\", \"w\", \"a\", each with a \"+\" that adds the\n # other direction, and a \"b\" anywhere after the letter that changes\n # nothing, as POSIX has no text mode. \"a\" is the kernel's append - every\n # write lands at the end whatever the position says, so two writers\n # appending to one log do not overwrite each other - and not a seek to\n # the end at open. *perm* is the mode a file the open creates gets, less\n # the umask; a file already there keeps its own. *buffer_size* stays\n # third so the callers that pass it by position keep working.\n #\n # One stream reads and writes through two buffers, so between a read and\n # a write in \"r+\" or \"w+\" move with `seek` (std/file), as C's stdio asks\n # of `fopen`'s \"+\" modes: a write after a read otherwise lands past the\n # read-ahead, and a read after a write misses what is still buffered.\n def self.open(path : String, mode : String = \"r\", buffer_size : Int32 = 4096, perm : Int32 = 420) : IyiIO\n\n def self.open(path : String, mode : String = \"r\", buffer_size : Int32 = 4096, perm : Int32 = 420, & : (IyiIO -> Nil)) : Nil\n\n def self.read(path : String) : String\n\n def self.write(path : String, contents : String) : Nil\nend\n" + }, + { + "name": "Float64", + "surface": "struct Float64 < Float\n\n def !=(other : Int32) : Bool\n\n def !=(other : Int64) : Bool\n\n def !=(other : UInt8) : Bool\n\n def !=(other : UInt64) : Bool\n\n def !=(other : Float64) : Bool\n\n def *(other : Int32) : self\n\n def *(other : Int64) : self\n\n def *(other : UInt8) : self\n\n def *(other : UInt64) : self\n\n def *(other : Float64) : self\n\n # A whole power by squaring in double-double - the power and the base each\n # a sum of two doubles good to some 106 bits - rounded once, at the end.\n # In plain doubles every step rounded: `1.1 ** 100` was 13780.612339822364\n # and `0.9999999 ** 10000000` 854,604 ulp off. A negative exponent powers\n # the reciprocal, itself a pair, so `2.0 ** -1074` is 5.0e-324 although\n # `2.0 ** 1074` is infinite, and `Int32::MIN` is counted in an Int64.\n def **(exponent : Int32) : Float64\n\n def +(other : Int32) : self\n\n def +(other : Int64) : self\n\n def +(other : UInt8) : self\n\n def +(other : UInt64) : self\n\n def +(other : Float64) : self\n\n def -(other : Int32) : self\n\n def -(other : Int64) : self\n\n def -(other : UInt8) : self\n\n def -(other : UInt64) : self\n\n def -(other : Float64) : self\n\n def /(other : Float64) : Float64\n\n def <(other : Int32) : Bool\n\n def <(other : Int64) : Bool\n\n def <(other : UInt8) : Bool\n\n def <(other : UInt64) : Bool\n\n def <(other : Float64) : Bool\n\n def <=(other : Int32) : Bool\n\n def <=(other : Int64) : Bool\n\n def <=(other : UInt8) : Bool\n\n def <=(other : UInt64) : Bool\n\n def <=(other : Float64) : Bool\n\n def ==(other : Int32) : Bool\n\n def ==(other : Int64) : Bool\n\n def ==(other : UInt8) : Bool\n\n def ==(other : UInt64) : Bool\n\n def ==(other : Float64) : Bool\n\n def >(other : Int32) : Bool\n\n def >(other : Int64) : Bool\n\n def >(other : UInt8) : Bool\n\n def >(other : UInt64) : Bool\n\n def >(other : Float64) : Bool\n\n def >=(other : Int32) : Bool\n\n def >=(other : Int64) : Bool\n\n def >=(other : UInt8) : Bool\n\n def >=(other : UInt64) : Bool\n\n def >=(other : Float64) : Bool\n\n # The sign cleared, which a comparison cannot do: `-0.0 < 0.0` is false,\n # and `(-0.0).abs` answered `-0.0` - `log10(-0.0)` came out `+Infinity`\n # for it. `copysign` is an instruction on every target.\n def abs : Float64\n\n def ceil : Float64\n\n def fdiv(other : Int32) : self\n\n def fdiv(other : Int64) : self\n\n def fdiv(other : UInt8) : self\n\n def fdiv(other : UInt64) : self\n\n def fdiv(other : Float64) : self\n\n # The integer below and above, as a `Float64`: one instruction where the\n # processor has it (`IyiRounding`), the conversion below where it does\n # not. Past 2^52 every double is an integer already and is answered as\n # it is, as NaN and the infinities are; the conversion is exact under\n # it. The answer carries the argument's sign, which is where a zero's\n # comes from - `(-0.3).ceil` is `-0.0`, as the instruction and the other\n # library answer.\n def floor : Float64\n\n # `-0.0 == 0.0`, so they hash alike. It was `std/float`'s, and a\n # `Hash(Float64, V)` without that import put every key in one slot.\n def hash : Int32\n\n # To `digits` places, a tie to the even one: scaled, rounded, scaled\n # back, so `3.14159.round(2)` is the double nearest 3.14. A negative\n # `digits` rounds to tens, hundreds, ...: `1234.0.round(-2)` is 1200.0,\n # and `25.0.round(-1)` is 20.0. The scale for those divides first rather\n # than multiplying by a fraction, which no double spells exactly.\n #\n # Where the scale leaves a double's range the answer is at hand, and the\n # arithmetic made Infinity or NaN of it: a double at 2^52 or past has no\n # fraction to round (`1e308.round(1)` was Infinity), a scale past the\n # range keeps every digit a double has (`1.5.round(400)` was NaN), and\n # one past the range the other way is a zero (`1.5.round(-400)`).\n #\n # A scaled value at 2^53 or past is at hand the same way: the place\n # `digits` names is finer than the double's last bit, so the nearest\n # double to the rounded decimal is the double itself. Scaled back, it\n # moved: `0.8173247445520655.round(17)` was 0.8173247445520654, and\n # `(-0.05).round(300)`, whose scale `10.0 ** 300` is not 10^300 exactly,\n # was -0.049999999999999996. Between 2^52 and 2^53 the place can still\n # be the coarser one, so that band is scaled back as before.\n def round(digits : Int32) : Float64\n\n # The nearest integer, a tie to the even one — Crystal's `round`, so the\n # name means one thing in both libraries (SPEC.md III.1.7a): `2.5.round`\n # is 2.0 and `3.5.round` is 4.0. `std/number` names the other modes.\n # The magnitude's fraction decides, not a sum: `(x + 0.5).floor` rounded\n # the sum first, and 0.49999999999999994 + 0.5 is 1.0. The sign goes\n # back by a multiplication, so `(-0.25).round` is -0.0, as there. A zero\n # is its own nearest integer and is answered as it is: `-0.0 < 0.0` is\n # false, and the sign test below answered `(-0.0).round` as 0.0.\n def round : Float64\n\n def to_f : Float64\n\n def to_f64 : Float64\n\n # The checked form raises on overflow — which in this prelude is a panic,\n # since `__crystal_raise_overflow` prints and exits (SPEC.md III.1.4).\n #\n # The unchecked form is the conversion without the check, and a float\n # past the range saturates (NaN is 0), alike in every build. It is\n # spelled the way `unsafe_chr` below is: `unsafe_to_u8`. Crystal spells\n # it `to_u8!`, which is not a name in iyi (SPEC.md III.1.7) — those\n # declarations were accepted only because a macro expansion was\n # reparsed by the other language's rules, and no iyi source could call\n # one: `x.to_u8!` is `x.to_u8` with its error propagated. The callers\n # wanted the instruction: `memset`'s `value.to_u8!` is C's \"converted to\n # unsigned char\", and the checked form panics on 0x1ff where C writes 0xff.\n def to_i : Int32\n\n def to_i32 : Int32\n\n def to_i64 : Int64\n\n def to_s : String\n\n def to_u64 : UInt64\n\n def to_u8 : UInt8\n\n def unsafe_to_f : Float64\n\n def unsafe_to_f64 : Float64\n\n def unsafe_to_i : Int32\n\n def unsafe_to_i32 : Int32\n\n def unsafe_to_i64 : Int64\n\n def unsafe_to_u64 : UInt64\n\n def unsafe_to_u8 : UInt8\n\n def self.zero : self\nend\n" + }, + { + "name": "Hash", + "surface": "# The one dictionary — SPEC.md, \"0.1.0\", item 3.\n#\n# Two dense arrays in insertion order, keys and values, and an index over\n# them: open addressing with linear probing from the key's hash to the\n# position of its entry, `-1` where a slot is free. The compact layout\n# Python's dictionary and the other library's `Hash` both settled on:\n# iteration follows the order things went in - the order a word count\n# prints its words - and the index is one array of integers rather than a\n# copy of the keys. Deletion marks the entry gone and its index slot a\n# tombstone, `-2`, which a probe steps over and an insert may take; the\n# dense arrays close their gaps when they next grow. Closing the gap at\n# once and rebuilding the index emptied a set of 20,000 in 0.95 s.\n#\n# Hashing is `key.hash`, defined per type in this prelude, and not iyi's\n# `Hashable` trait: that lives in `src/std`, the opt-in library, and a\n# prelude that depended on it would have the dependency backwards.\nclass Hash(K, V)\n\n # Two tables are equal when they hold the same pairs, in any order, as the\n # other library's are: `Reference#==` asked whether they were one object,\n # so `{\"a\" => 1} == {\"a\" => 1}` was false and a table could not be found\n # as a key by an equal one.\n def ==(other : Hash(K, V)) : Bool\n\n def [](key : K) : V\n\n def []=(key : K, value : V) : V\n\n def []?(key : K) : V | ::Nil\n\n def all?(& : (K, V -> Bool)) : Bool\n\n def any?(& : (K, V -> Bool)) : Bool\n\n # Every entry forgotten, and the room with them; a default stays.\n def clear : Hash(K, V)\n\n # The pairs whose value is not nil, in a table whose value type says so.\n def compact\n\n # The questions a table gets asked of its pairs, each block handed the key\n # and the value.\n def count(& : (K, V -> Bool)) : Int32\n\n # The value the key had, or nil. The entry is marked gone where it\n # stands, so the order of what remains is the order it went in, and its\n # index slot becomes a tombstone. `each` starts past the gone at the\n # front: from entry 0, taking the oldest key and deleting it until none\n # was left stepped over every one gone before, 197 ms for 40,000.\n def delete(key : K) : V | ::Nil\n\n # The value the key had, deleted, or what the block answers for a key\n # that was not there.\n def delete(key : K, & : (K -> U)) : V | U forall U\n\n # `table.dig(\"a\", \"b\")`: the value at the first key, then the value at the\n # next key inside that, for every key given. A value that cannot be dug\n # into with the keys left is refused, naming the key that led to it, and\n # a missing key is refused as `[]` refuses it; `dig?` answers nil for both.\n def dig(key : K)\n\n def dig(key : K, *subkeys)\n\n def dig?(key : K)\n\n def dig?(key : K, *subkeys)\n\n # A table of its own with the same pairs in the same order, and the same\n # default; what is in the keys and values is shared, as an array's dup\n # shares its elements.\n def dup : Hash(K, V)\n\n # Every pair, in the order it went in.\n def each(& : (K, V -> Nil)) : Nil\n\n def each_key(& : (K -> Nil)) : Nil\n\n def each_value(& : (V -> Nil)) : Nil\n\n def empty? : Bool\n\n # The value, or *default* when the key has none: the call says what a\n # missing key answers, so a default the table was made with is not asked.\n def fetch(key : K, default : D) : V | D forall D\n\n # The value, or what the block answers for the missing key.\n def fetch(key : K, & : (K -> U)) : V | U forall U\n\n def first_key : K\n\n # The oldest pair's key and value, nil when the table is empty; `first_key`\n # and `first_value` refuse an empty table as `Array#first` refuses one.\n def first_key? : K | ::Nil\n\n def first_value : V\n\n def first_value? : V | ::Nil\n\n def has_key?(key : K) : Bool\n\n # Whether a value is in the table: a walk, since the index is of keys.\n def has_value?(value : V) : Bool\n\n # Hashing follows `==`, so the order cannot count: each pair is hashed as\n # a tuple is, mixed, and the pairs added up, which any order adds alike.\n def hash : Int32\n\n def inspect : String\n\n # Values to keys; a value two keys share keeps the later key.\n def invert : Hash(V, K)\n\n def key_for(value : V) : K\n\n # The first key, in insertion order, whose value is *value*; `key_for`\n # refuses a value no key has, and `key_for?` answers nil for it.\n def key_for?(value : V) : K | ::Nil\n\n # The keys, and the values, each in the order they went in.\n def keys : Array(K)\n\n # An array of what the block answers for each pair, in order.\n def map(& : (K, V -> U)) : Array(U) forall U\n\n # A new table with this one's pairs and then *other*'s, the second value\n # winning for a key both have; the key and value types are both tables'.\n # Neither table changes, and the copy has no default: one made for these\n # keys and values need not answer for the wider types.\n def merge(other : Hash(L, W)) : Hash(K | L, V | W) forall L, W\n\n # The same, with the block answering for a key both tables have: it is\n # handed the key, this table's value and *other*'s.\n def merge(other : Hash(L, W), & : (L, V | W, W -> V | W)) : Hash(K | L, V | W) forall L, W\n\n # *other*'s pairs written into this table, which is answered: the other\n # library's `merge!`, spelled as SPEC.md III.1.7a spells the mutating one.\n def merge_in_place(other : Hash(L, W)) : Hash(K, V) forall L, W\n\n def merge_in_place(other : Hash(L, W), & : (L, V, W -> V)) : Hash(K, V) forall L, W\n\n def new\n\n def new(&block : (Hash(K, V), K -> V))\n\n # The key's value when it has one, and otherwise *value* written and\n # answered: one probe for a key that is there.\n def put_if_absent(key : K, value : V) : V\n\n # The same with the value computed only when the key is missing.\n def put_if_absent(key : K, & : (K -> V)) : V\n\n def reject(& : (K, V -> Bool)) : Hash(K, V)\n\n def reject_in_place(& : (K, V -> Bool)) : Hash(K, V)\n\n # The pairs the block answers true for, or false for, as a new table.\n def select(& : (K, V -> Bool)) : Hash(K, V)\n\n # The same, in place. A delete inside `each` is safe in this table: it\n # marks the entry gone where it stands and nothing moves until a growth,\n # which only a write of a new key starts.\n def select_in_place(& : (K, V -> Bool)) : Hash(K, V)\n\n # The oldest pair taken out and answered, the table drained as a queue in\n # the order it was filled; `shift?` answers nil when it is empty.\n def shift : Tuple(K, V)\n\n def shift? : Tuple(K, V) | ::Nil\n\n def size : Int32\n\n # The pairs sorted on the key the block answers for each, ascending and\n # stable: `to_a.sorted_by` with the pair taken apart for the block.\n def sorted_by(& : (K, V -> U)) : Array(Tuple(K, V)) forall U\n\n # The sum of what the block answers for each pair, and the block's own\n # zero when there are none, as `Array#sum` answers.\n def sum(& : (K, V -> U)) : U forall U\n\n # Every pair as a tuple, in the order it went in - what a sort by value\n # starts from.\n def to_a : Array(Tuple(K, V))\n\n # The table itself, as the other library's `to_h` answers it.\n def to_h : Hash(K, V)\n\n # `{\"a\" => 1}`, in insertion order, both sides inspected; a first key in\n # braces is spaced off, `{ {1, 2} => 3 }`, as a tuple's: `{{` is a macro.\n def to_s : String\n\n # Every key through the block, in order; two keys that become one keep the\n # later value, as two writes of one key do.\n def transform_keys(& : (K, V -> L)) : Hash(L, V) forall L\n\n # Every value through the block, the keys and their order kept; the block\n # may take the key as well, after the value, as the other library's does.\n def transform_values(& : (V, K -> W)) : Hash(K, W) forall W\n\n # The same in place, which is why the type cannot change.\n def transform_values_in_place(& : (V, K -> V)) : Hash(K, V)\n\n # The key's value replaced by what the block answers for it, and the old\n # value answered, as the other library answers it. A missing key is handed\n # to the table's default and the block answers for that; with no default\n # it is refused as `[]` refuses it.\n def update(key : K, & : (V -> V)) : V\n\n def values : Array(V)\n\n # The values of the keys given, in that order, each answered as `[]`\n # answers it - so a missing key is refused, or the default's.\n def values_at(*keys : K) : Array(V)\n\n def self.new(default : V) : Hash(K, V)\nend\n" + }, + { + "name": "Int32", + "surface": "# The comparison cross is the one `UInt16` joins, and only it: the\n# conversions above stay at five, because `std/int` declares the whole\n# tower's conversions and a prelude that also declared `UInt16#to_i32`\n# would be redefining one of them (SPEC.md R-3, which is how this split\n# announced itself).\n#\n# A wide character against a double is left out for the same reason and\n# a better one: `std/int` defines that comparison by converting, because\n# every value narrower than a double *is* a double exactly, and an\n# instruction that compares the two directly is not what it means. A 64-bit\n# one is not, and the instruction rounded it first: `9007199254740993_i64 ==\n# 9007199254740992.0` was true. Split at 2048, its high part is a double\n# exactly, and the difference below keeps the values' sign.\nstruct Int32 < Int\n\n def !=(other : Int32) : Bool\n\n def !=(other : Int64) : Bool\n\n def !=(other : UInt8) : Bool\n\n def !=(other : UInt64) : Bool\n\n def !=(other : Float64) : Bool\n\n def !=(other : UInt16) : Bool\n\n def %(other : Int32) : Int32\n\n def &(other : Int32) : self\n\n def &(other : Int64) : self\n\n def &(other : UInt8) : self\n\n def &(other : UInt64) : self\n\n def &*(other : Int32) : self\n\n def &*(other : Int64) : self\n\n def &*(other : UInt8) : self\n\n def &*(other : UInt64) : self\n\n def &+(other : Int32) : self\n\n def &+(other : Int64) : self\n\n def &+(other : UInt8) : self\n\n def &+(other : UInt64) : self\n\n def &-(other : Int32) : self\n\n def &-(other : Int64) : self\n\n def &-(other : UInt8) : self\n\n def &-(other : UInt64) : self\n\n def *(other : Int32) : self\n\n def *(other : Int64) : self\n\n def *(other : UInt8) : self\n\n def *(other : UInt64) : self\n\n def *(other : Float64) : Float64\n\n # A whole power, by squaring: `2 ** 10`. A negative exponent is a\n # mistake for an integer, as division by zero is.\n def **(exponent : Int32) : Int32\n\n def +(other : Int32) : self\n\n def +(other : Int64) : self\n\n def +(other : UInt8) : self\n\n def +(other : UInt64) : self\n\n def +(other : Float64) : Float64\n\n def -(other : Int32) : self\n\n def -(other : Int64) : self\n\n def -(other : UInt8) : self\n\n def -(other : UInt64) : self\n\n def -(other : Float64) : Float64\n\n def - : Int32\n\n def /(other : Float64) : Float64\n\n # The zero is checked here rather than left to the processor. `1 // 0` used\n # to reach `unsafe_div`, trap, and kill the program with \"Process terminated\n # because of a floating-point system exception\" — an integer division\n # reported by the operating system as a floating-point fault, which is a\n # sentence nobody can act on.\n def //(other : Int32) : Int32\n\n def <(other : Int32) : Bool\n\n def <(other : Int64) : Bool\n\n def <(other : UInt8) : Bool\n\n def <(other : UInt64) : Bool\n\n def <(other : Float64) : Bool\n\n def <(other : UInt16) : Bool\n\n # Shifts, and the magnitude: what a bit of arithmetic past the four\n # operators asks for first. A shift by the width or more answers zero (the\n # sign, for a negative shifted right), which the processor does not\n # promise, and a negative count shifts the other way, as `8 >> -2` is 32.\n def <<(count : Int32) : Int32\n\n def <=(other : Int32) : Bool\n\n def <=(other : Int64) : Bool\n\n def <=(other : UInt8) : Bool\n\n def <=(other : UInt64) : Bool\n\n def <=(other : Float64) : Bool\n\n def <=(other : UInt16) : Bool\n\n def ==(other : Int32) : Bool\n\n def ==(other : Int64) : Bool\n\n def ==(other : UInt8) : Bool\n\n def ==(other : UInt64) : Bool\n\n def ==(other : Float64) : Bool\n\n def ==(other : UInt16) : Bool\n\n def >(other : Int32) : Bool\n\n def >(other : Int64) : Bool\n\n def >(other : UInt8) : Bool\n\n def >(other : UInt64) : Bool\n\n def >(other : Float64) : Bool\n\n def >(other : UInt16) : Bool\n\n def >=(other : Int32) : Bool\n\n def >=(other : Int64) : Bool\n\n def >=(other : UInt8) : Bool\n\n def >=(other : UInt64) : Bool\n\n def >=(other : Float64) : Bool\n\n def >=(other : UInt16) : Bool\n\n def >>(count : Int32) : Int32\n\n def ^(other : Int32) : self\n\n def ^(other : Int64) : self\n\n def ^(other : UInt8) : self\n\n def ^(other : UInt64) : self\n\n def abs : Int32\n\n # The character with this code point: the other half of `Char#ord`.\n def chr : Char\n\n def even? : Bool\n\n def hash : Int32\n\n def odd? : Bool\n\n def times(& : (Int32 -> Nil)) : Nil\n\n def to_f : Float64\n\n def to_f64 : Float64\n\n # The checked form raises on overflow — which in this prelude is a panic,\n # since `__crystal_raise_overflow` prints and exits (SPEC.md III.1.4).\n #\n # The unchecked form is the conversion without the check, and a float\n # past the range saturates (NaN is 0), alike in every build. It is\n # spelled the way `unsafe_chr` below is: `unsafe_to_u8`. Crystal spells\n # it `to_u8!`, which is not a name in iyi (SPEC.md III.1.7) — those\n # declarations were accepted only because a macro expansion was\n # reparsed by the other language's rules, and no iyi source could call\n # one: `x.to_u8!` is `x.to_u8` with its error propagated. The callers\n # wanted the instruction: `memset`'s `value.to_u8!` is C's \"converted to\n # unsigned char\", and the checked form panics on 0x1ff where C writes 0xff.\n def to_i : Int32\n\n def to_i32 : Int32\n\n def to_i64 : Int64\n\n def to_s(base : Int32) : String\n\n def to_s : String\n\n def to_u64 : UInt64\n\n def to_u8 : UInt8\n\n def unsafe_chr : Char\n\n def unsafe_div(other : Int32) : self\n\n def unsafe_div(other : Int64) : self\n\n def unsafe_div(other : UInt8) : self\n\n def unsafe_div(other : UInt64) : self\n\n def unsafe_mod(other : Int32) : self\n\n def unsafe_mod(other : Int64) : self\n\n def unsafe_mod(other : UInt8) : self\n\n def unsafe_mod(other : UInt64) : self\n\n def unsafe_shl(other : Int32) : self\n\n def unsafe_shl(other : Int64) : self\n\n def unsafe_shl(other : UInt8) : self\n\n def unsafe_shl(other : UInt64) : self\n\n def unsafe_shr(other : Int32) : self\n\n def unsafe_shr(other : Int64) : self\n\n def unsafe_shr(other : UInt8) : self\n\n def unsafe_shr(other : UInt64) : self\n\n def unsafe_to_f : Float64\n\n def unsafe_to_f64 : Float64\n\n def unsafe_to_i : Int32\n\n def unsafe_to_i32 : Int32\n\n def unsafe_to_i64 : Int64\n\n def unsafe_to_u64 : UInt64\n\n def unsafe_to_u8 : UInt8\n\n def |(other : Int32) : self\n\n def |(other : Int64) : self\n\n def |(other : UInt8) : self\n\n def |(other : UInt64) : self\n\n def self.zero : self\nend\n\n# `import std/int` gives Int32 the rest of the integer tower's surface:\n# arithmetic and comparison with every integer type, `//`, `%`, `<<`, `>>`,\n# `<=>`, the bit counts and every `to_*` conversion (`iyi doc std/int`).\n" + }, + { + "name": "Int64", + "surface": "# A 64-bit signed integer, the literal `1_i64`.\nstruct Int64 < Int\n\n def !=(other : Int32) : Bool\n\n def !=(other : Int64) : Bool\n\n def !=(other : UInt8) : Bool\n\n def !=(other : UInt64) : Bool\n\n def !=(other : Float64) : Bool\n\n def !=(other : UInt16) : Bool\n\n def %(other : Int64) : Int64\n\n def &(other : Int32) : self\n\n def &(other : Int64) : self\n\n def &(other : UInt8) : self\n\n def &(other : UInt64) : self\n\n def &*(other : Int32) : self\n\n def &*(other : Int64) : self\n\n def &*(other : UInt8) : self\n\n def &*(other : UInt64) : self\n\n def &+(other : Int32) : self\n\n def &+(other : Int64) : self\n\n def &+(other : UInt8) : self\n\n def &+(other : UInt64) : self\n\n def &-(other : Int32) : self\n\n def &-(other : Int64) : self\n\n def &-(other : UInt8) : self\n\n def &-(other : UInt64) : self\n\n def *(other : Int32) : self\n\n def *(other : Int64) : self\n\n def *(other : UInt8) : self\n\n def *(other : UInt64) : self\n\n def *(other : Float64) : Float64\n\n def +(other : Int32) : self\n\n def +(other : Int64) : self\n\n def +(other : UInt8) : self\n\n def +(other : UInt64) : self\n\n def +(other : Float64) : Float64\n\n def -(other : Int32) : self\n\n def -(other : Int64) : self\n\n def -(other : UInt8) : self\n\n def -(other : UInt64) : self\n\n def -(other : Float64) : Float64\n\n def - : Int64\n\n def /(other : Float64) : Float64\n\n def //(other : Int64) : Int64\n\n def <(other : Int32) : Bool\n\n def <(other : Int64) : Bool\n\n def <(other : UInt8) : Bool\n\n def <(other : UInt64) : Bool\n\n def <(other : Float64) : Bool\n\n def <(other : UInt16) : Bool\n\n def <<(count : Int32) : Int64\n\n def <=(other : Int32) : Bool\n\n def <=(other : Int64) : Bool\n\n def <=(other : UInt8) : Bool\n\n def <=(other : UInt64) : Bool\n\n def <=(other : Float64) : Bool\n\n def <=(other : UInt16) : Bool\n\n def ==(other : Int32) : Bool\n\n def ==(other : Int64) : Bool\n\n def ==(other : UInt8) : Bool\n\n def ==(other : UInt64) : Bool\n\n def ==(other : Float64) : Bool\n\n def ==(other : UInt16) : Bool\n\n def >(other : Int32) : Bool\n\n def >(other : Int64) : Bool\n\n def >(other : UInt8) : Bool\n\n def >(other : UInt64) : Bool\n\n def >(other : Float64) : Bool\n\n def >(other : UInt16) : Bool\n\n def >=(other : Int32) : Bool\n\n def >=(other : Int64) : Bool\n\n def >=(other : UInt8) : Bool\n\n def >=(other : UInt64) : Bool\n\n def >=(other : Float64) : Bool\n\n def >=(other : UInt16) : Bool\n\n def >>(count : Int32) : Int64\n\n def ^(other : Int32) : self\n\n def ^(other : Int64) : self\n\n def ^(other : UInt8) : self\n\n def ^(other : UInt64) : self\n\n def abs : Int64\n\n # The halves mixed, as `UInt64`'s are. Without it an `Int64` hashed to\n # its type, `Object#hash`'s default, so every key of a `Hash(Int64, V)`\n # or `Set(Int64)` went to one slot and each insert walked all the keys\n # before it: 80,000 inserts took 2.9 s and two million did not finish in\n # two minutes. `UInt64`, `UInt8`, `Char` and `Float64` were the same,\n # and `std/int`, which writes `hash` for the rest of the integer tower,\n # leaves the prelude's types to the prelude.\n def hash : Int32\n\n def to_f : Float64\n\n def to_f64 : Float64\n\n # The checked form raises on overflow — which in this prelude is a panic,\n # since `__crystal_raise_overflow` prints and exits (SPEC.md III.1.4).\n #\n # The unchecked form is the conversion without the check, and a float\n # past the range saturates (NaN is 0), alike in every build. It is\n # spelled the way `unsafe_chr` below is: `unsafe_to_u8`. Crystal spells\n # it `to_u8!`, which is not a name in iyi (SPEC.md III.1.7) — those\n # declarations were accepted only because a macro expansion was\n # reparsed by the other language's rules, and no iyi source could call\n # one: `x.to_u8!` is `x.to_u8` with its error propagated. The callers\n # wanted the instruction: `memset`'s `value.to_u8!` is C's \"converted to\n # unsigned char\", and the checked form panics on 0x1ff where C writes 0xff.\n def to_i : Int32\n\n def to_i32 : Int32\n\n def to_i64 : Int64\n\n # Writes digits backward using division on negative numbers so that\n # Int64::MIN (-9223372036854775808) does not overflow negation. The\n # digits are counted first and written straight into the string: they\n # went through an `Array(Int32)` of their own, an allocation and a\n # growth per number printed, and printing two million integers took\n # twice Go's time.\n def to_s(base : Int32) : String\n\n # Written by dividing the *negative* magnitude rather than negating it,\n # because `-Int64::MIN` is not an Int64: negation overflows and every\n # digit after it is wrong. `src/std/format.iyi` used to carry a second\n # definition of both of these with this shape, so what a program got\n # depended on whether the formatter had been loaded; there is one of each\n # now, here, where `Int32#to_s` already delegates.\n def to_s : String\n\n def to_u64 : UInt64\n\n def to_u8 : UInt8\n\n def unsafe_chr : Char\n\n def unsafe_div(other : Int32) : self\n\n def unsafe_div(other : Int64) : self\n\n def unsafe_div(other : UInt8) : self\n\n def unsafe_div(other : UInt64) : self\n\n def unsafe_mod(other : Int32) : self\n\n def unsafe_mod(other : Int64) : self\n\n def unsafe_mod(other : UInt8) : self\n\n def unsafe_mod(other : UInt64) : self\n\n def unsafe_shl(other : Int32) : self\n\n def unsafe_shl(other : Int64) : self\n\n def unsafe_shl(other : UInt8) : self\n\n def unsafe_shl(other : UInt64) : self\n\n def unsafe_shr(other : Int32) : self\n\n def unsafe_shr(other : Int64) : self\n\n def unsafe_shr(other : UInt8) : self\n\n def unsafe_shr(other : UInt64) : self\n\n def unsafe_to_f : Float64\n\n def unsafe_to_f64 : Float64\n\n def unsafe_to_i : Int32\n\n def unsafe_to_i32 : Int32\n\n def unsafe_to_i64 : Int64\n\n def unsafe_to_u64 : UInt64\n\n def unsafe_to_u8 : UInt8\n\n def |(other : Int32) : self\n\n def |(other : Int64) : self\n\n def |(other : UInt8) : self\n\n def |(other : UInt64) : self\n\n def self.zero : self\nend\n\n# `import std/int` gives Int64 the rest of the integer tower's surface:\n# arithmetic and comparison with every integer type, `//`, `%`, `<<`, `>>`,\n# `<=>`, the bit counts and every `to_*` conversion (`iyi doc std/int`).\n" + }, + { + "name": "Nil", + "surface": "# The absence of a value, and the only value of its type.\nstruct Nil < Value\n\n def ==(other : Nil) : Bool\n\n def inspect : String\n\n def to_s : String\nend\n" + }, + { + "name": "Object", + "surface": "# What the compiler emits instructions for — SPEC.md, \"0.1.0\", item 3.\n#\n# A `@[Primitive]` method has no body on purpose: the annotation names an\n# instruction the code generator writes at the call site. So this file is not\n# an implementation, it is the list of machine operations iyi admits, and its\n# shape is a language decision rather than a library one.\n#\n# **This is iyi's list, not Crystal's.** `src/primitives.cr` declares twelve\n# numeric types and crosses them with each other, which is 2,580 definitions\n# expanded on every build and **0.11 s of a 0.17 s front end** — measured by\n# deleting the block and building again. iyi crosses five, which is 445.\n#\n# The five are `Int32`, `Int64`, `UInt8`, `UInt64` and `Float64`: the default\n# integer, the one a byte count grows into, the byte itself, the one an address\n# and a size are, and a float. `UInt16` joins them for comparison alone, for\n# the reason written at the cross below: a wide character is compared on\n# every Windows path. `Int8`, `Int16`, `Int128` and the unsigned middle exist\n# as types and have no arithmetic, because no sample writes one — the same\n# rule the rest of the prelude is built on. Writing `1_i8 + 1_i8` is an\n# \"undefined method\" and that is the honest answer: 0.1.0 does not have\n# eight-bit arithmetic rather than having it slowly.\n#\n# **Comparison is not refused the same way, and that is a defect this file\n# cannot close.** `Object#==` answers `false` for anything no type claimed,\n# which is right about two objects and a lie about two numbers: without\n# `import std/int`, `1_i8 == 1_i8` is `false` rather than an error, and it\n# was `92_u16 == 92_u16` answering false that hid two Windows defects for a\n# week. Refusing it needs both sides' types at once, which a method body\n# does not have — a guard on the receiver alone refuses `5 == nil`, where\n# `false` is the correct answer, and it was measured doing exactly that to\n# eleven exercises. The fix belongs in overload resolution, where the\n# compiler knows both types; until then `UInt16` being in the cross is what\n# the library's own callers needed, and this paragraph is the rest of the truth.\nclass Object\n\n def !=(other) : Bool\n\n def ==(other) : Bool\n\n # Case equality, what a `when` asks of each arm: equality, unless a type\n # answers with something stronger - a range, a class, a tuple.\n def ===(other) : Bool\n\n def class\n\n # Every type is usable as a `Hash` key, and one that has not said how it\n # hashes hashes to its type: correct and useless, every value of the type\n # in one slot, the honest default. A type used as a key defines `hash`.\n def hash : Int32\n\n # `value.in?(a, b)` — the Kemal port's way of writing an alternation. On\n # `Object` because it is about the value, not about any one type of it.\n def in?(*values : *T) : Bool forall T\n\n # What a value looks like written down rather than printed: the two differ\n # for `nil` and strings, and `#{value.inspect}` shows a value is absent.\n def inspect : String\n\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" + }, + { + "name": "Panicked", + "surface": "# What a *caught* panic answers as — III.1.4's \"catchable at task\n# boundaries\", made literal: reading a panicked task's value delivers\n# this instead of a re-raise, and III.1.3's machinery (`case`, `!`,\n# `.or`) handles it like any other failure. A panic nobody reads still\n# re-raises at the join: observed bugs are values, unobserved bugs are\n# loud.\nstruct Panicked\n\n def message : String\n\n def new(text : String)\n\n def text : String\nend\n" + }, + { + "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.\n def copy_from(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", + "surface": "function Proc(*T, R) < Value\n\n def call(*args : *T) : R\nend\n" + }, + { + "name": "Program", + "surface": "# The running program: its arguments and its environment, on demand.\nclass Program\n\n # The arguments after the program's own name, the way every shell\n # hands them over.\n def self.args : Array(String)\n\n # The directory the process is in, which is not what `PWD` says.\n # `PWD` is a variable a shell keeps: cmd and PowerShell never set it,\n # so on Windows every program that expanded a relative path panicked\n # (\"no base given and PWD is not set\"), and on POSIX it is a copy that\n # a program's own `Dir.cd` leaves behind. Nil when the platform has no\n # answer — wasm32-wasi resolves a path against a preopened directory\n # and names none — or when the directory is gone from under the\n # process.\n #\n # 4096 bytes covers POSIX's PATH_MAX and Windows' MAX_PATH; the second\n # try is for a Windows path built with `\\\\?\\`, which reaches 32,767\n # characters and up to three UTF-8 bytes each.\n def self.cwd : String | Nil\n\n # One variable, or nil. No map of everything: the asking program names\n # what it wants, and a tool that dumps its whole environment into a\n # structure was one `puts` away from leaking it.\n def self.env(name : String) : String | Nil\nend\n" + }, + { + "name": "Range", + "surface": "# Ranges — SPEC.md, \"0.1.0\", item 3.\n#\n# The compiler expands a range literal to `Range.new(from, to, exclusive)`\n# (literal_expander.cr) and predefines the type to refer to it; with no\n# methods, `(1..10).each` was refused with \"wrong number of arguments for\n# 'Range(B, E).new'\", a constructor nobody wrote. A range here counts up by\n# one and stops: no `Enumerable`, no `step`, no reverse iteration.\nstruct Range(B, E)\n\n # A range is a value, so two written the same way are the same range:\n # `Object#==` is identity, and `(1..3) == (1..3)` was false. `1..3` and\n # `1...3` differ, and that is the flag's whole job.\n def ==(other : self) : Bool\n\n # `when 1..5` is `(1..5) === x`: inclusion. `==` answered, and no arm matched.\n def ===(value) : Bool\n\n def begin : B\n\n # The inclusive walk stops *at* the end rather than stepping past it: with\n # `value <= @end` the last step left the type on the checked `+`, so\n # `(2147483645..2147483647).each` panicked with \"arithmetic overflow\" at\n # the value it was asked to yield last.\n def each(& : (B -> Nil)) : Nil\n\n def end : E\n\n # `1...10` excludes its end, `1..10` includes it.\n def exclusive? : Bool\n\n # And hashing follows equality, or equal ranges would land in two slots.\n def hash : Int32\n\n # The three a range is written for: a value per position, the positions\n # that answer, and the total.\n def map(& : (B -> U)) : Array(U) forall U\n\n def new(begin __arg0 : B, end __arg1 : E, exclusive : Bool)\n\n def select(& : (B -> Bool)) : Array(B)\n\n def size : Int32\n\n def sum : B\n\n def to_a : Array(B)\n\n # `1..5` and `1...3`, each end inspected and a nil one left out, as the\n # other library writes one; `Object#to_s` printed `Range(Int32, Int32)`.\n def to_s : String\nend\n" + }, + { + "name": "Reference", + "surface": "# What every class instance is: a pointer to its fields, compared and\n# hashed by identity unless the class says otherwise.\n#\n# That was this comment's promise and nothing kept it: `Object#==` answers\n# false, so an instance was not even equal to itself, and a `Set(Node)`\n# never found the node just added to it nor a `Hash(Node, V)` its key.\n# Identity is `==` now, and the hash is the address. A class that writes\n# its own `==` says what equal means, and the address would part two\n# instances it calls equal - so one that writes `==` and no `hash` keeps\n# the type's id, correct for any `==` and one slot for every instance, the\n# default `Object#hash` explains.\nclass Reference\n\n def ==(other : Reference) : Bool\n\n def hash : Int32\n\n def new\n\n def object_id : UInt64\nend\n" + }, + { + "name": "Set", + "surface": "# The one set — \"have I seen this\", with nothing attached to the answer.\n#\n# A `Hash` whose values are nothing: the same dense keys in insertion\n# order and the same index over them, so `add`, `includes?` and `delete`\n# cost what the hash's do and iteration reads back in the order things\n# went in. It is a wrapper rather than a copy of `hash.iyi` because the\n# index is the part worth getting right once. The sample that asked for\n# it (`samples/iyi/visited.iyi`) walks a graph with a cycle in it, and\n# `add` answering whether the element was new is the one bit that walk\n# needs.\nclass Set(T)\n\n # Not in `other`, in this order; `other` may be of another element type.\n def -(other : Set(U)) : Set(T) forall U\n\n # True when the element was not there before.\n def add(value : T) : Bool\n\n # True when the element was there.\n def delete(value : T) : Bool\n\n # Every element, in the order it went in.\n def each(& : (T -> Nil)) : Nil\n\n def empty? : Bool\n\n def includes?(value : T) : Bool\n\n def inspect : String\n\n def new\n\n def size : Int32\n\n def to_a : Array(T)\n\n # `Set{\"a\", \"b\"}`, in insertion order, elements inspected.\n def to_s : String\nend\n" + }, + { + "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" + }, + { + "name": "Struct", + "surface": "# A struct is its fields, as the other library's `Struct#==` has it: two are\n# equal when each field is. `Object#==` answers false, so a struct was not\n# equal to itself - `b == b` was false and `{b => 1}[b]?` nil. `Object#hash`\n# stays correct for it, one slot a type; a struct that is a key writes one.\nstruct Struct < Value\n\n def ==(other : self) : Bool\nend\n" + }, + { + "name": "Symbol", + "surface": "# A name that compares by identity: `:ok`, `:err`.\nstruct Symbol < Value\n\n def !=(other : Symbol) : Bool\n\n def ==(other : Symbol) : Bool\n\n def hash : Int32\n\n def to_i : Int32\n\n def to_s : String\nend\n" + }, + { + "name": "Tuple", + "surface": "# A tuple written the way it is written: `{1, \"a\"}`. The compiler lays a\n# tuple out and answers `[]`; what it looks like printed is the prelude's,\n# one element per position, each inspected.\ntuple Tuple(*T) < Value\n\n # Two tuples are the same value when their members are. `Object#==` is\n # false, so `{1, 2} == {1, 2}` was, and a tuple key was never found in a\n # `Hash`. Member by member where one type holds the other, so `case {1,\n # y} when {1, 2}` (y : Int32?) matches; `{-1}` and `{-1_i64}` are unequal.\n def ==(other : Tuple(*U)) : Bool forall U\n\n # Each member's own `===`: `when {Nil, Bool}` holds for `{nil, false}`,\n # which `==` - a class against a value - never did.\n def ===(other : Tuple(*U)) : Bool forall U\n\n # Indexed by a *value* rather than by a literal, which is what walking a\n # tuple at runtime needs: `std/indexable`'s `values_at` and `sprintf` do.\n def [](index : Int32)\n\n # And hashing follows equality: a key is found by its slot first and\n # compared second, and `Object#hash` put every tuple in one slot. Each\n # member multiplied through and its high bits folded back down: `a * 31 +\n # b` gave the million points of a 1,000 by 1,000 grid 32,000 hashes, and\n # a table probing a slot at a time had not filled in ten minutes.\n def hash : Int32\n\n def inspect : String\n\n # How many: the type's own arithmetic, here rather than in `format.iyi`,\n # where `std/indexable` could only reach it with a formatter compiled in.\n def size : Int32\n\n # A first member in braces of its own is spaced off, `{ {1, 2}, 3 }`, as the other library's is.\n def to_s : String\nend\n" + }, + { + "name": "TypeCastError", + "surface": "# A failed `.as` in `select` expansion is a compiler bug; this panic carries\n# the constant's name rather than an exception type.\nclass TypeCastError\n\n def message : String\n\n def new(message : String)\nend\n" + }, + { + "name": "UInt16", + "surface": "struct UInt16 < Int\n\n def !=(other : Int32) : Bool\n\n def !=(other : Int64) : Bool\n\n def !=(other : UInt8) : Bool\n\n def !=(other : UInt64) : Bool\n\n def !=(other : UInt16) : Bool\n\n def <(other : Int32) : Bool\n\n def <(other : Int64) : Bool\n\n def <(other : UInt8) : Bool\n\n def <(other : UInt64) : Bool\n\n def <(other : UInt16) : Bool\n\n def <=(other : Int32) : Bool\n\n def <=(other : Int64) : Bool\n\n def <=(other : UInt8) : Bool\n\n def <=(other : UInt64) : Bool\n\n def <=(other : UInt16) : Bool\n\n def ==(other : Int32) : Bool\n\n def ==(other : Int64) : Bool\n\n def ==(other : UInt8) : Bool\n\n def ==(other : UInt64) : Bool\n\n def ==(other : UInt16) : Bool\n\n def >(other : Int32) : Bool\n\n def >(other : Int64) : Bool\n\n def >(other : UInt8) : Bool\n\n def >(other : UInt64) : Bool\n\n def >(other : UInt16) : Bool\n\n def >=(other : Int32) : Bool\n\n def >=(other : Int64) : Bool\n\n def >=(other : UInt8) : Bool\n\n def >=(other : UInt64) : Bool\n\n def >=(other : UInt16) : Bool\nend\n\n# `import std/int` gives UInt16 the rest of the integer tower's surface:\n# arithmetic and comparison with every integer type, `//`, `%`, `<<`, `>>`,\n# `<=>`, the bit counts and every `to_*` conversion (`iyi doc std/int`).\n" + }, + { + "name": "UInt64", + "surface": "struct UInt64 < Int\n\n def !=(other : Int32) : Bool\n\n def !=(other : Int64) : Bool\n\n def !=(other : UInt8) : Bool\n\n def !=(other : UInt64) : Bool\n\n def !=(other : Float64) : Bool\n\n def !=(other : UInt16) : Bool\n\n def %(other : UInt64) : UInt64\n\n def &(other : Int32) : self\n\n def &(other : Int64) : self\n\n def &(other : UInt8) : self\n\n def &(other : UInt64) : self\n\n def &*(other : Int32) : self\n\n def &*(other : Int64) : self\n\n def &*(other : UInt8) : self\n\n def &*(other : UInt64) : self\n\n def &+(other : Int32) : self\n\n def &+(other : Int64) : self\n\n def &+(other : UInt8) : self\n\n def &+(other : UInt64) : self\n\n def &-(other : Int32) : self\n\n def &-(other : Int64) : self\n\n def &-(other : UInt8) : self\n\n def &-(other : UInt64) : self\n\n def *(other : Int32) : self\n\n def *(other : Int64) : self\n\n def *(other : UInt8) : self\n\n def *(other : UInt64) : self\n\n def *(other : Float64) : Float64\n\n def +(other : Int32) : self\n\n def +(other : Int64) : self\n\n def +(other : UInt8) : self\n\n def +(other : UInt64) : self\n\n def +(other : Float64) : Float64\n\n def -(other : Int32) : self\n\n def -(other : Int64) : self\n\n def -(other : UInt8) : self\n\n def -(other : UInt64) : self\n\n def -(other : Float64) : Float64\n\n def //(other : UInt64) : UInt64\n\n def <(other : Int32) : Bool\n\n def <(other : Int64) : Bool\n\n def <(other : UInt8) : Bool\n\n def <(other : UInt64) : Bool\n\n def <(other : Float64) : Bool\n\n def <(other : UInt16) : Bool\n\n def <=(other : Int32) : Bool\n\n def <=(other : Int64) : Bool\n\n def <=(other : UInt8) : Bool\n\n def <=(other : UInt64) : Bool\n\n def <=(other : Float64) : Bool\n\n def <=(other : UInt16) : Bool\n\n def ==(other : Int32) : Bool\n\n def ==(other : Int64) : Bool\n\n def ==(other : UInt8) : Bool\n\n def ==(other : UInt64) : Bool\n\n def ==(other : Float64) : Bool\n\n def ==(other : UInt16) : Bool\n\n def >(other : Int32) : Bool\n\n def >(other : Int64) : Bool\n\n def >(other : UInt8) : Bool\n\n def >(other : UInt64) : Bool\n\n def >(other : Float64) : Bool\n\n def >(other : UInt16) : Bool\n\n def >=(other : Int32) : Bool\n\n def >=(other : Int64) : Bool\n\n def >=(other : UInt8) : Bool\n\n def >=(other : UInt64) : Bool\n\n def >=(other : Float64) : Bool\n\n def >=(other : UInt16) : Bool\n\n def ^(other : Int32) : self\n\n def ^(other : Int64) : self\n\n def ^(other : UInt8) : self\n\n def ^(other : UInt64) : self\n\n # The high half multiplied into the low: a key that fits 32 bits hashes\n # to itself, as an `Int32` does, and a plain XOR of the halves would put\n # `(n << 32) | n` at zero for every `n`.\n def hash : Int32\n\n def to_f : Float64\n\n def to_f64 : Float64\n\n # The checked form raises on overflow — which in this prelude is a panic,\n # since `__crystal_raise_overflow` prints and exits (SPEC.md III.1.4).\n #\n # The unchecked form is the conversion without the check, and a float\n # past the range saturates (NaN is 0), alike in every build. It is\n # spelled the way `unsafe_chr` below is: `unsafe_to_u8`. Crystal spells\n # it `to_u8!`, which is not a name in iyi (SPEC.md III.1.7) — those\n # declarations were accepted only because a macro expansion was\n # reparsed by the other language's rules, and no iyi source could call\n # one: `x.to_u8!` is `x.to_u8` with its error propagated. The callers\n # wanted the instruction: `memset`'s `value.to_u8!` is C's \"converted to\n # unsigned char\", and the checked form panics on 0x1ff where C writes 0xff.\n def to_i : Int32\n\n def to_i32 : Int32\n\n def to_i64 : Int64\n\n def to_s(base : Int32) : String\n\n def to_s : String\n\n def to_u64 : UInt64\n\n def to_u8 : UInt8\n\n def unsafe_chr : Char\n\n def unsafe_div(other : Int32) : self\n\n def unsafe_div(other : Int64) : self\n\n def unsafe_div(other : UInt8) : self\n\n def unsafe_div(other : UInt64) : self\n\n def unsafe_mod(other : Int32) : self\n\n def unsafe_mod(other : Int64) : self\n\n def unsafe_mod(other : UInt8) : self\n\n def unsafe_mod(other : UInt64) : self\n\n def unsafe_shl(other : Int32) : self\n\n def unsafe_shl(other : Int64) : self\n\n def unsafe_shl(other : UInt8) : self\n\n def unsafe_shl(other : UInt64) : self\n\n def unsafe_shr(other : Int32) : self\n\n def unsafe_shr(other : Int64) : self\n\n def unsafe_shr(other : UInt8) : self\n\n def unsafe_shr(other : UInt64) : self\n\n def unsafe_to_f : Float64\n\n def unsafe_to_f64 : Float64\n\n def unsafe_to_i : Int32\n\n def unsafe_to_i32 : Int32\n\n def unsafe_to_i64 : Int64\n\n def unsafe_to_u64 : UInt64\n\n def unsafe_to_u8 : UInt8\n\n def |(other : Int32) : self\n\n def |(other : Int64) : self\n\n def |(other : UInt8) : self\n\n def |(other : UInt64) : self\n\n def self.zero : self\nend\n\n# `import std/int` gives UInt64 the rest of the integer tower's surface:\n# arithmetic and comparison with every integer type, `//`, `%`, `<<`, `>>`,\n# `<=>`, the bit counts and every `to_*` conversion (`iyi doc std/int`).\n" + }, + { + "name": "UInt8", + "surface": "# The unsigned pair, which the prelude did not declare: `UInt64`'s division\n# and modulo, and text for both. They arrived inside `format.iyi` because\n# `sprintf` needed them, so a program dividing two `UInt64`s had to have\n# loaded a formatter to do it - which `std/time` found the moment the\n# formatter stopped being everybody's.\nstruct UInt8 < Int\n\n def !=(other : Int32) : Bool\n\n def !=(other : Int64) : Bool\n\n def !=(other : UInt8) : Bool\n\n def !=(other : UInt64) : Bool\n\n def !=(other : Float64) : Bool\n\n def !=(other : UInt16) : Bool\n\n def &(other : Int32) : self\n\n def &(other : Int64) : self\n\n def &(other : UInt8) : self\n\n def &(other : UInt64) : self\n\n def &*(other : Int32) : self\n\n def &*(other : Int64) : self\n\n def &*(other : UInt8) : self\n\n def &*(other : UInt64) : self\n\n def &+(other : Int32) : self\n\n def &+(other : Int64) : self\n\n def &+(other : UInt8) : self\n\n def &+(other : UInt64) : self\n\n def &-(other : Int32) : self\n\n def &-(other : Int64) : self\n\n def &-(other : UInt8) : self\n\n def &-(other : UInt64) : self\n\n def *(other : Int32) : self\n\n def *(other : Int64) : self\n\n def *(other : UInt8) : self\n\n def *(other : UInt64) : self\n\n def *(other : Float64) : Float64\n\n def +(other : Int32) : self\n\n def +(other : Int64) : self\n\n def +(other : UInt8) : self\n\n def +(other : UInt64) : self\n\n def +(other : Float64) : Float64\n\n def -(other : Int32) : self\n\n def -(other : Int64) : self\n\n def -(other : UInt8) : self\n\n def -(other : UInt64) : self\n\n def -(other : Float64) : Float64\n\n def <(other : Int32) : Bool\n\n def <(other : Int64) : Bool\n\n def <(other : UInt8) : Bool\n\n def <(other : UInt64) : Bool\n\n def <(other : Float64) : Bool\n\n def <(other : UInt16) : Bool\n\n def <=(other : Int32) : Bool\n\n def <=(other : Int64) : Bool\n\n def <=(other : UInt8) : Bool\n\n def <=(other : UInt64) : Bool\n\n def <=(other : Float64) : Bool\n\n def <=(other : UInt16) : Bool\n\n def ==(other : Int32) : Bool\n\n def ==(other : Int64) : Bool\n\n def ==(other : UInt8) : Bool\n\n def ==(other : UInt64) : Bool\n\n def ==(other : Float64) : Bool\n\n def ==(other : UInt16) : Bool\n\n def >(other : Int32) : Bool\n\n def >(other : Int64) : Bool\n\n def >(other : UInt8) : Bool\n\n def >(other : UInt64) : Bool\n\n def >(other : Float64) : Bool\n\n def >(other : UInt16) : Bool\n\n def >=(other : Int32) : Bool\n\n def >=(other : Int64) : Bool\n\n def >=(other : UInt8) : Bool\n\n def >=(other : UInt64) : Bool\n\n def >=(other : Float64) : Bool\n\n def >=(other : UInt16) : Bool\n\n def ^(other : Int32) : self\n\n def ^(other : Int64) : self\n\n def ^(other : UInt8) : self\n\n def ^(other : UInt64) : self\n\n def hash : Int32\n\n def to_f : Float64\n\n def to_f64 : Float64\n\n # The checked form raises on overflow — which in this prelude is a panic,\n # since `__crystal_raise_overflow` prints and exits (SPEC.md III.1.4).\n #\n # The unchecked form is the conversion without the check, and a float\n # past the range saturates (NaN is 0), alike in every build. It is\n # spelled the way `unsafe_chr` below is: `unsafe_to_u8`. Crystal spells\n # it `to_u8!`, which is not a name in iyi (SPEC.md III.1.7) — those\n # declarations were accepted only because a macro expansion was\n # reparsed by the other language's rules, and no iyi source could call\n # one: `x.to_u8!` is `x.to_u8` with its error propagated. The callers\n # wanted the instruction: `memset`'s `value.to_u8!` is C's \"converted to\n # unsigned char\", and the checked form panics on 0x1ff where C writes 0xff.\n def to_i : Int32\n\n def to_i32 : Int32\n\n def to_i64 : Int64\n\n def to_s(base : Int32) : String\n\n def to_s : String\n\n def to_u64 : UInt64\n\n def to_u8 : UInt8\n\n def unsafe_chr : Char\n\n def unsafe_div(other : Int32) : self\n\n def unsafe_div(other : Int64) : self\n\n def unsafe_div(other : UInt8) : self\n\n def unsafe_div(other : UInt64) : self\n\n def unsafe_mod(other : Int32) : self\n\n def unsafe_mod(other : Int64) : self\n\n def unsafe_mod(other : UInt8) : self\n\n def unsafe_mod(other : UInt64) : self\n\n def unsafe_shl(other : Int32) : self\n\n def unsafe_shl(other : Int64) : self\n\n def unsafe_shl(other : UInt8) : self\n\n def unsafe_shl(other : UInt64) : self\n\n def unsafe_shr(other : Int32) : self\n\n def unsafe_shr(other : Int64) : self\n\n def unsafe_shr(other : UInt8) : self\n\n def unsafe_shr(other : UInt64) : self\n\n def unsafe_to_f : Float64\n\n def unsafe_to_f64 : Float64\n\n def unsafe_to_i : Int32\n\n def unsafe_to_i32 : Int32\n\n def unsafe_to_i64 : Int64\n\n def unsafe_to_u64 : UInt64\n\n def unsafe_to_u8 : UInt8\n\n def |(other : Int32) : self\n\n def |(other : Int64) : self\n\n def |(other : UInt8) : self\n\n def |(other : UInt64) : self\n\n def self.zero : self\nend\n\n# `import std/int` gives UInt8 the rest of the integer tower's surface:\n# arithmetic and comparison with every integer type, `//`, `%`, `<<`, `>>`,\n# `<=>`, the bit counts and every `to_*` conversion (`iyi doc std/int`).\n" + } + ] + } +} diff --git a/scripts/budget.json b/scripts/budget.json index 03a6197..004a100 100644 --- a/scripts/budget.json +++ b/scripts/budget.json @@ -36,7 +36,10 @@ { "match": "/learn/*/", "ceiling": 245760 }, { "match": "/playground/", "ceiling": 229376 }, { "match": "/playground/iyi/*/", "ceiling": 262144 }, - { "match": "/playground/*/", "ceiling": 294912 } + { "match": "/playground/*/", "ceiling": 294912 }, + { "match": "/reference/", "ceiling": 245760 }, + { "match": "/reference/std/*/", "ceiling": 344064 }, + { "match": "/reference/prelude/*/", "ceiling": 229376 } ], "assets": 638976 } diff --git a/scripts/prose.mjs b/scripts/prose.mjs index 300534e..be5336f 100755 --- a/scripts/prose.mjs +++ b/scripts/prose.mjs @@ -42,8 +42,17 @@ const INLINE = "a|code|strong|em|b|i|span|abbr|kbd|dfn"; // Blocks whose insides are a token stream rather than prose. Matched by class // because that is what the site's own CSS keys on, so a new block type that // wants the exemption has to say so in the same vocabulary. +// +// `doc-comment` is the one block here that is prose, and it is exempt for the +// other half of this gate's reasoning: the defect is a template's line break +// that Astro drops, and a doc comment on a reference page reaches the HTML as +// one string out of the repository, with no template line between its words. +// What it does contain is the vocabulary of a standard library, `RFC 6265bis`, +// `0xc0`, `1xx`, which the figure check reads as a number welded to a word. +// Each one wraps exactly one paragraph or list, never a listing, so the +// first closing tag after it is its own. const LISTINGS = - /<(pre|figure|div)[^>]*class="[^"]*(?:source|console|diagnostic|editor-ink|editor|cases|case|stamped|recording)[^"]*"[\s\S]*?<\/\1>/g; + /<(pre|figure|div)[^>]*class="[^"]*(?:source|console|diagnostic|editor-ink|editor|cases|case|stamped|recording|doc-comment)[^"]*"[\s\S]*?<\/\1>/g; function htmlFiles(dir) { const found = []; diff --git a/scripts/record-stdlib.mjs b/scripts/record-stdlib.mjs new file mode 100644 index 0000000..14a604e --- /dev/null +++ b/scripts/record-stdlib.mjs @@ -0,0 +1,159 @@ +#!/usr/bin/env node +// Records the standard library's exported surface, as the compiler prints it. +// +// The reference under /reference/ is this record. Every module under +// src/std/ and every type the prelude declares is put through `iyi doc`, the +// verb that prints a module's exported surface with its doc comments, and +// what it printed is kept byte for byte with the digest of the source it was +// printed from. +// +// WHY THE COMPILER AND NOT A PARSER HERE. What a module exports is the +// compiler's answer: `pub`, a reopened prelude type whose methods export +// without it, a macro, a `forall`. A second reading of iyi's grammar in this +// repository would be a second answer, and the first time the two disagreed +// the reference would document a function nobody can call. `iyi doc` is also +// the surface an agent is given over MCP, so the page and the tool say the +// same thing. +// +// WHY `iyi doc` AND NOT `iyi mod dump --json`. The JSON leaves out a reopened +// type's methods and a module's constants, and most of std/int, std/text and +// std/set is exactly that. The text surface is the complete one. +// +// Regenerate with: npm run record:stdlib + +import { createHash } from "node:crypto"; +import { spawnSync } from "node:child_process"; +import { existsSync, mkdirSync, writeFileSync } from "node:fs"; +import { arch, cpus, release as osRelease, type as osType } from "node:os"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { + blobAt, + releaseRef, + requireReleaseCompiler, + requireReleaseTree, + treeAt, +} 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 out = resolve(site, "records", "stdlib.json"); + +// --------------------------------------------------------------------------- +// Toolchain and provenance +// --------------------------------------------------------------------------- + +/* The binary is `iyi` on a Unix build and `iyi.exe` on Windows, where the + * release ships a zip; IYI_BUILD may name either a build directory or the + * `bin/` of an unpacked release. */ +const buildDir = process.env.IYI_BUILD ? resolve(process.env.IYI_BUILD) : resolve(repo, ".build"); +const iyi = ["iyi", "iyi.exe"].map((name) => join(buildDir, name)).find(existsSync); +if (iyi === undefined) { + throw new Error( + `no iyi compiler in ${buildDir}. Build it, or set IYI_BUILD to the ` + + `directory holding the iyi binary.`, + ); +} + +// Evidence about a release, taken from the release: the tree is the tag and +// the compiler was built there. scripts/release-ref.mjs says why. +const release = releaseRef(repo); +requireReleaseTree(repo, release, "record:stdlib"); +const version = requireReleaseCompiler(iyi, release, "record:stdlib"); + +const recorded = { + compiler: version, + commit: release.commit, + machine: `${osType()} ${osRelease()} ${arch()}, ${cpus()[0]?.model.trim() ?? "unknown cpu"}`, + command: "npm run record:stdlib", + when: new Date().toISOString(), +}; +for (const [key, value] of Object.entries(recorded)) { + if (!value) throw new Error(`provenance field "${key}" came out empty`); +} + +const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex"); + +/* One `iyi doc` call. The std imports resolve through the tree being + * recorded, not whatever library sits beside the binary. */ +const env = { ...process.env, IYI_PATH: resolve(repo, "src") }; +function doc(target) { + const run = spawnSync(iyi, ["doc", target], { cwd: repo, env, encoding: "utf8" }); + if (run.status !== 0) { + throw new Error( + `iyi doc ${target} exited ${run.status}:\n${run.stderr || run.stdout}`, + ); + } + return run.stdout.replace(/\r\n/g, "\n"); +} + +// --------------------------------------------------------------------------- +// The modules: every file directly under src/std, as the release ships it +// --------------------------------------------------------------------------- + +/* A file one directory down (src/std/eiy/process.iyi) is a module's private + * helper, declared under a name of its own and not imported as std/anything, + * so the modules are the files directly under src/std. Each one declares + * `module std/`, and one that does not is refused rather than + * filed under a name it does not answer to. */ +const paths = treeAt(repo, release.tag, "src/std").filter((path) => + /^src\/std\/[^/]+\.iyi$/.test(path), +); +if (paths.length === 0) { + throw new Error(`${release.tag} has no src/std/*.iyi, so there is no library to record`); +} + +const modules = paths.map((path) => { + const bytes = blobAt(repo, release.tag, path); + const name = `std/${path.slice("src/std/".length, -".iyi".length)}`; + const declared = /^module\s+(\S+)/m.exec(bytes.toString("utf8"))?.[1]; + if (declared !== name) { + throw new Error(`${path} declares module ${declared ?? "nothing"}, not ${name}`); + } + const surface = doc(path); + if (!surface.startsWith(`module ${name}\n`)) { + throw new Error(`iyi doc ${path} did not open with "module ${name}":\n${surface.slice(0, 200)}`); + } + return { name, path, sourceSha256: sha256(bytes), surface }; +}); + +// --------------------------------------------------------------------------- +// The prelude: every type `iyi doc prelude` lists, one surface each +// --------------------------------------------------------------------------- + +/* `iyi doc prelude` prints one line per type, `class String # Text ...`, + * and `iyi doc String` prints that type's surface. The list is the + * compiler's, so a type the prelude gains reaches the reference without an + * edit here. */ +const index = doc("prelude"); +const typeNames = [ + ...new Set( + index + .split("\n") + .map((line) => /^[a-z]+\s+([A-Z]\w*)/.exec(line)?.[1]) + .filter((name) => name !== undefined), + ), +]; +if (typeNames.length === 0) { + throw new Error(`iyi doc prelude listed no types:\n${index.slice(0, 400)}`); +} +const types = typeNames.map((name) => ({ name, surface: doc(name) })); + +/* What the prelude was printed from, so the build can tell a record of an + * older prelude from this one. */ +const preludeSources = treeAt(repo, release.tag, "src/iyi") + .filter((path) => path.endsWith(".iyi")) + .map((path) => ({ path, sha256: sha256(blobAt(repo, release.tag, path)) })); + +mkdirSync(dirname(out), { recursive: true }); +writeFileSync( + out, + `${JSON.stringify({ recorded, modules, prelude: { sources: preludeSources, types } }, null, 2)}\n`, + "utf8", +); + +console.log( + `stdlib: ${modules.length} modules and ${types.length} prelude types, ` + + `at ${release.commit.slice(0, 9)}`, +); diff --git a/scripts/release-ref.mjs b/scripts/release-ref.mjs index e3121c5..2d4ba8d 100644 --- a/scripts/release-ref.mjs +++ b/scripts/release-ref.mjs @@ -168,10 +168,17 @@ export function requireReleaseTree(repo, release, verb) { * from a developer's branch is caught here rather than in a recording that * names a release and was produced by something else. The provenance line on * every page carries this string. + * + * A build in a worktree prints nine hex digits; the release's own binary, the + * one install.sh unpacks, prints seven (`iyi 0.18.0 (built on Crystal ... + * [2a96530] ...)`). Both name the commit, so a bracketed hash the tag's commit + * starts with is accepted beside the nine-digit spelling. */ export function requireReleaseCompiler(iyi, release, verb) { const version = execFileSync(iyi, ["--version"], { encoding: "utf8" }).split("\n")[0].trim(); if (version.includes(release.commit.slice(0, 9))) return version; + const named = /\[([0-9a-f]{7,40})\]/.exec(version)?.[1]; + if (named !== undefined && release.commit.startsWith(named)) return version; throw new Error( `${iyi} prints "${version}", which does not name ${release.commit.slice(0, 9)}, ` + `the commit ${release.tag} is. ${verb} would then stamp a release onto a ` + diff --git a/scripts/stdlib.mjs b/scripts/stdlib.mjs new file mode 100644 index 0000000..c72445d --- /dev/null +++ b/scripts/stdlib.mjs @@ -0,0 +1,351 @@ +#!/usr/bin/env node +// The standard library reference, checked against the release and cut into +// pages. +// +// records/stdlib.json is what `iyi doc` printed for every module under +// src/std and every prelude type, recorded by `npm run record:stdlib` at the +// release's tag. The Pages build has no compiler, so this script does not +// make the record: it refuses one that is not about the release the site +// publishes, or that was printed from source the release does not ship, and +// then writes src/generated/stdlib.json, the record cut into what a page +// renders. +// +// WHAT IS THE COMPILER'S AND WHAT IS THE SOURCE'S. Every signature and every +// doc comment on a reference page is in the recorded surface, verbatim. The +// one thing no compiler output carries is a module's header comment, the +// block above `module std/...`, so that is read out of the source at the tag, +// whose digest the record already pins. +// +// WHAT THIS PARSES. Not iyi. The surface is a listing: optional `#` lines, +// then a declaration at the left margin, then whatever is indented under it, +// then an `end` at the margin if the declaration opened a block. That shape is +// cut into one item per declaration, so each type and each top-level function +// gets a heading and an address. The declaration's own text goes to the page +// unchanged; the cut only decides where one item stops. + +import { createHash } from "node:crypto"; +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { escape } from "./markdown.mjs"; +import { blobAt, releaseRef, treeAt } 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 recordPath = resolve(site, "records", "stdlib.json"); +const out = resolve(site, "src", "generated", "stdlib.json"); + +const release = releaseRef(repo); +const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex"); + +const problems = []; +const problem = (message) => problems.push(message); +const again = "Regenerate with npm run record:stdlib, at the release's tag."; + +if (!existsSync(recordPath)) { + throw new Error(`records/stdlib.json is not there. ${again}`); +} +const record = JSON.parse(readFileSync(recordPath, "utf8")); + +// --------------------------------------------------------------------------- +// The record is about this release, and about what it ships +// --------------------------------------------------------------------------- + +for (const field of ["compiler", "commit", "machine", "command", "when"]) { + if (typeof record.recorded?.[field] !== "string" || record.recorded[field] === "") { + problem(`records/stdlib.json: provenance field "${field}" is empty`); + } +} +if (record.recorded?.commit !== release.commit) { + problem( + `records/stdlib.json was recorded at ${String(record.recorded?.commit).slice(0, 9)} ` + + `and the site publishes ${release.tag}, ${release.commit.slice(0, 9)}. ${again}`, + ); +} + +const shipped = treeAt(repo, release.tag, "src/std").filter((path) => + /^src\/std\/[^/]+\.iyi$/.test(path), +); +const recordedPaths = new Set(record.modules.map((module) => module.path)); +for (const path of shipped) { + if (!recordedPaths.has(path)) problem(`${path} is in ${release.tag} and not in the record. ${again}`); +} +for (const module of record.modules) { + const bytes = blobAt(repo, release.tag, module.path); + if (bytes === null) { + problem(`records/stdlib.json documents ${module.path}, which ${release.tag} does not ship. ${again}`); + } else if (sha256(bytes) !== module.sourceSha256) { + problem(`${module.path} at ${release.tag} is not the file its surface was printed from. ${again}`); + } +} + +const preludeShipped = treeAt(repo, release.tag, "src/iyi").filter((path) => path.endsWith(".iyi")); +const preludeRecorded = new Map(record.prelude.sources.map((source) => [source.path, source.sha256])); +for (const path of preludeShipped) { + const digest = preludeRecorded.get(path); + if (digest === undefined) { + problem(`${path} is in ${release.tag} and the prelude record does not name it. ${again}`); + } else if (digest !== sha256(blobAt(repo, release.tag, path))) { + problem(`${path} at ${release.tag} is not the prelude the record was printed from. ${again}`); + } +} +if (preludeRecorded.size !== preludeShipped.length) { + problem(`the prelude record names files ${release.tag} does not ship. ${again}`); +} + +if (problems.length > 0) { + throw new Error(`stdlib:\n ${problems.join("\n ")}`); +} + +// --------------------------------------------------------------------------- +// Comments into blocks +// --------------------------------------------------------------------------- + +/* The inline spans doc comments use: code in backticks, an argument's name in + * single asterisks. Code first and whole, because what is inside a code span + * is not markup. Nothing else is read as markup: `a[i][j]` outside backticks + * is text, not a reference link. */ +function inline(text) { + const spans = []; + let html = text.replace(/`([^`]+)`/g, (_, code) => { + spans.push(`${escape(code)}`); + return `\u0000${spans.length - 1}\u0000`; + }); + html = escape(html).replace(/(^|[\s(])\*([^*\s][^*]*)\*(?=[\s.,;:)]|$)/g, "$1$2"); + return html.replace(/\u0000(\d+)\u0000/g, (_, index) => spans[Number(index)]); +} + +/** Comment text, its `#` and one space already gone, as page blocks. */ +function blocksOf(lines) { + const blocks = []; + let paragraph = []; + let list = null; + let code = null; + let fenced = false; + + /* A rule drawn in the comment (`── Channel — III.4.6 ───`, `---- lazy + * adaptors ----`) is the source file's furniture, a divider between + * stretches of code, and on a page it would be a paragraph of dashes. */ + const DIVIDER = /──|^-{3,}|^={3,}/; + const flush = () => { + const text = paragraph.join(" "); + if (paragraph.length > 0 && !DIVIDER.test(text)) blocks.push({ kind: "paragraph", html: inline(text) }); + if (list !== null) blocks.push({ kind: "list", items: list.map((item) => inline(item.join(" "))) }); + if (code !== null) blocks.push({ kind: "code", text: code.join("\n").replace(/\n+$/, "") }); + paragraph = []; + list = null; + code = null; + }; + + for (const line of lines) { + if (fenced) { + if (/^```/.test(line)) { + fenced = false; + flush(); + } else code.push(line); + continue; + } + if (/^```/.test(line)) { + flush(); + fenced = true; + code = []; + continue; + } + if (line.trim() === "") { + flush(); + continue; + } + const item = /^[*-]\s+(.*)$/.exec(line); + if (item) { + if (list === null) { + flush(); + list = []; + } + list.push([item[1]]); + continue; + } + if (list !== null && /^ {2}\S/.test(line)) { + list[list.length - 1].push(line.trim()); + continue; + } + if (/^\s{2,}\S/.test(line)) { + if (code === null) { + flush(); + code = []; + } + code.push(line.replace(/^ {2}/, "")); + continue; + } + if (code !== null || list !== null) flush(); + paragraph.push(line.trim()); + } + flush(); + return blocks; +} + +const uncomment = (line) => line.replace(/^#\s?/, ""); + +/** The first sentence of a comment, as plain text, or null where it has none. */ +function sentenceOf(blocks) { + const first = blocks.find((block) => block.kind === "paragraph"); + if (!first) return null; + const text = first.html + .replace(/<[^>]+>/g, "") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, '"') + .replace(/&/g, "&"); + const sentence = /^(.+?[.!?])(?:\s|$)/.exec(text)?.[1] ?? text; + return sentence.trim(); +} + +// --------------------------------------------------------------------------- +// A surface into items +// --------------------------------------------------------------------------- + +const DECLARATION = + /^(?:(?:pub|protected|private)\s+)?(?:abstract\s+)?(struct|class|trait|enum|module|lib|impl|annotation|alias|type|def|macro|fun|metaclass|function|tuple)\s+(.+)$/; + +/** What a declaration line is, and what to call it in a heading. */ +function headingOf(line) { + const match = DECLARATION.exec(line); + if (!match) return { kind: "declaration", name: line.trim() }; + const [, kind, rest] = match; + if (kind === "impl") return { kind, name: rest.trim() }; + if (kind === "def" || kind === "macro" || kind === "fun") { + return { kind, name: /^(\S+?)(?:\(|\s|$)/.exec(rest)?.[1] ?? rest }; + } + const name = /^(::)?([^\s(<]+(?:\([^)]*\))?)/.exec(rest); + return { + kind: name?.[1] ? `${kind}, reopened` : kind, + name: name?.[2] ?? rest, + }; +} + +/** The listing cut at its left margin into one item per declaration. */ +function itemsOf(surface) { + const lines = surface.replace(/\n+$/, "").split("\n"); + const items = []; + let comment = []; + let at = 0; + while (at < lines.length) { + const line = lines[at]; + if (line === "") { + comment = []; + at += 1; + continue; + } + if (line.startsWith("#")) { + comment.push(uncomment(line)); + at += 1; + continue; + } + /* A declaration, then what is indented under it, blank lines included + * where more indentation follows, then its `end`. */ + const body = [line]; + at += 1; + while (at < lines.length) { + const next = lines[at]; + if (/^\s/.test(next)) { + body.push(next); + at += 1; + } else if (next === "") { + const ahead = lines.slice(at).find((candidate) => candidate !== ""); + if (ahead !== undefined && /^\s/.test(ahead)) { + body.push(next); + at += 1; + } else break; + } else break; + } + if (lines[at] === "end") { + body.push("end"); + at += 1; + } + items.push({ ...headingOf(line), doc: blocksOf(comment), code: body.join("\n") }); + comment = []; + } + return items; +} + +/** An address per item, unique on its page. */ +function withIds(items) { + const taken = new Map(); + return items.map((item) => { + const base = + (item.kind === "impl" ? `impl-${item.name}` : item.name) + .replace(/[^A-Za-z0-9_?!]+/g, "-") + .replace(/[?!]/g, (c) => (c === "?" ? "-q" : "-bang")) + .replace(/^-+|-+$/g, "") || "item"; + const count = (taken.get(base) ?? 0) + 1; + taken.set(base, count); + return { id: count === 1 ? base : `${base}-${count}`, ...item }; + }); +} + +// --------------------------------------------------------------------------- +// The pages +// --------------------------------------------------------------------------- + +const modules = record.modules.map((module) => { + const source = blobAt(repo, release.tag, module.path).toString("utf8").replace(/\r\n/g, "\n"); + const lines = source.split("\n"); + const declaredAt = lines.findIndex((line) => /^module\s/.test(line)); + const header = lines + .slice(0, declaredAt) + .filter((line) => line.startsWith("#") || line.trim() === "") + .map((line) => (line.startsWith("#") ? uncomment(line) : "")); + const intro = blocksOf(header); + const surface = module.surface.slice(module.surface.indexOf("\n") + 1); + return { + name: module.name, + slug: module.name.slice("std/".length), + path: module.path, + summary: sentenceOf(intro), + intro, + items: withIds(itemsOf(surface)), + }; +}); + +const prelude = record.prelude.types.map((type) => { + const items = withIds(itemsOf(type.surface)); + /* A type's surface is one declaration, sometimes followed by the same + * type's reopening in another prelude file. The first item's comment is + * the type's own and becomes the page's introduction. */ + const intro = items[0]?.doc ?? []; + return { + name: type.name, + slug: type.name.toLowerCase(), + summary: sentenceOf(intro), + intro, + items: items.map((item, index) => (index === 0 ? { ...item, doc: [] } : item)), + }; +}); + +const slugs = new Set(); +for (const page of [...modules.map((m) => `std/${m.slug}`), ...prelude.map((p) => `prelude/${p.slug}`)]) { + if (slugs.has(page)) throw new Error(`stdlib: two reference pages would be at /reference/${page}/`); + slugs.add(page); +} + +mkdirSync(dirname(out), { recursive: true }); +writeFileSync( + out, + `${JSON.stringify( + { + release: { version: release.version, tag: release.tag, commit: release.commit }, + recorded: record.recorded, + modules, + prelude, + }, + null, + 2, + )}\n`, + "utf8", +); + +const declarations = [...modules, ...prelude].reduce((n, page) => n + page.items.length, 0); +console.log( + `stdlib: ${modules.length} modules and ${prelude.length} prelude types, ` + + `${declarations} declarations, checked against ${release.tag}`, +); diff --git a/src/components/ReferenceBody.astro b/src/components/ReferenceBody.astro new file mode 100644 index 0000000..9f6fe37 --- /dev/null +++ b/src/components/ReferenceBody.astro @@ -0,0 +1,184 @@ +--- +/** + * One reference page's body: the header comment, a list of what the page + * declares, then each declaration under its own heading with its doc comment + * and its listing exactly as `iyi doc` printed it. + * + * Every listing is a `.source` figure, which is the site's listing face and + * exempts it from scripts/prose.mjs: a signature is a token stream, not prose. + */ +import type { Block, Item } from "../lib/reference"; + +interface Props { + /** What the listings are, for their captions: `std/csv`, `String`. */ + name: string; + /** The command a reader would run to see the same text. */ + command: string; + intro: Block[]; + items: Item[]; +} + +const { name, command, intro, items } = Astro.props; + +/* The introduction's first paragraph is the page's lead, set above it by the + * page, so it is not printed twice. */ +const firstParagraph = intro.findIndex((block) => block.kind === "paragraph"); +const rest = intro.filter((_, index) => index !== firstParagraph); +--- + +{ + rest.length > 0 && ( +
+ {rest.map((block) => + block.kind === "paragraph" ? ( +

+ ) : block.kind === "list" ? ( +
    {block.items.map((item) =>
  • )}
+ ) : ( +
+
+
{block.text}
+
+
+ ), + )} +
+ ) +} + +{ + items.length > 1 && ( +
+ ) +} + +
+ { + items.map((item) => ( +
+

+ {item.name} + {item.kind} +

+ {item.doc.map((block) => + block.kind === "paragraph" ? ( +

+ ) : block.kind === "list" ? ( +
    {block.items.map((entry) =>
  • )}
+ ) : ( +
+
+
{block.text}
+
+
+ ), + )} +
+
+ {name} + {command} +
+
+
{item.code}
+
+
+
+ )) + } +
+ + diff --git a/src/lib/reference.ts b/src/lib/reference.ts new file mode 100644 index 0000000..8880acf --- /dev/null +++ b/src/lib/reference.ts @@ -0,0 +1,90 @@ +/** + * The standard library reference: what `iyi doc` printed for every std + * module and every prelude type at the release, cut into pages by + * scripts/stdlib.mjs. That script says what is the compiler's and what is the + * source's; this file only names the pages and says what a results page is + * told about each. + */ +import stdlib from "../generated/stdlib.json"; + +/** A comment, as blocks: a paragraph or a list as HTML, code as text. */ +export type Block = + | { kind: "paragraph"; html: string } + | { kind: "list"; items: string[] } + | { kind: "code"; text: string }; + +/** One declaration: its heading, its doc comment, and its listing verbatim. */ +export interface Item { + id: string; + kind: string; + name: string; + doc: Block[]; + code: string; +} + +export interface ReferencePage { + name: string; + slug: string; + /** The first sentence of the header comment, or null where there is none. */ + summary: string | null; + intro: Block[]; + items: Item[]; +} + +export interface ModulePage extends ReferencePage { + /** Repository relative path of the module's source. */ + path: string; +} + +export const reference = stdlib as unknown as { + release: { version: string; tag: string; commit: string }; + recorded: { compiler: string; commit: string; machine: string; command: string; when: string }; + modules: ModulePage[]; + prelude: ReferencePage[]; +}; + +const base = import.meta.env.BASE_URL.replace(/\/*$/, "/"); + +export const referenceHref = (): string => `${base}reference/`; +export const moduleHref = (page: ModulePage): string => `${base}reference/std/${page.slug}/`; +export const typeHref = (page: ReferencePage): string => `${base}reference/prelude/${page.slug}/`; + +/* A results page's description: the header comment's first sentence when it + * is one a reader can use, then what the page is. A first sentence that cites + * SPEC.md or the README is written for someone holding the repository + * ("Text, SPEC.md, item 3"), so it is left to the page and the generic + * description stands in. Either way it has to fit the 155 characters the + * layout allows. */ +const DESCRIPTION_MAX = 155; +const usable = (summary: string | null): summary is string => + summary !== null && !/SPEC\.md|README/.test(summary); + +function describe(summary: string | null, what: string, fallback: string): string { + if (usable(summary)) { + const joined = `${summary} ${what}`; + if (joined.length <= DESCRIPTION_MAX) return joined; + } + return fallback; +} + +export function moduleSearch(page: ModulePage): { title: string; description: string } { + return { + title: `${page.name} module, iyi standard library`, + description: describe( + page.summary, + `Reference for ${page.name} in the iyi standard library.`, + `Reference for ${page.name} in the iyi standard library: every exported type and function, with its doc comments.`, + ), + }; +} + +export function typeSearch(page: ReferencePage): { title: string; description: string } { + return { + title: `${page.name} methods in iyi: prelude reference`, + description: describe( + page.summary, + `Every method iyi's prelude gives ${page.name}.`, + `The ${page.name} type in iyi: every method the prelude gives it, with its signature and doc comment.`, + ), + }; +} diff --git a/src/pages/llms.txt.ts b/src/pages/llms.txt.ts index 8e825af..7f40782 100644 --- a/src/pages/llms.txt.ts +++ b/src/pages/llms.txt.ts @@ -17,6 +17,7 @@ import proofs from "../generated/proofs.json"; import { SPELLED } from "../lib/lessons"; import agent from "../../records/agent.json"; import readme from "../generated/readme.json"; +import { reference } from "../lib/reference"; export const GET: APIRoute = async ({ site }) => { const origin = (site ?? new URL("https://iyi.dev")).href.replace(/\/$/, ""); @@ -135,6 +136,7 @@ an iyi module; iyi consumes Crystal, not the other way around. - [Home](${origin}/): the one line, a listing that runs, the Crystal section. - [Why iyi](${origin}/why/): the four sides, each with its measurement and machine. - [Learn](${origin}/learn/): ${SPELLED[lessons.length] ?? lessons.length} lessons on the rule, each running a real program. +- [Reference](${origin}/reference/): the standard library, ${reference.prelude.length} prelude types and ${reference.modules.length} std modules, as \`iyi doc\` prints them. - [Install](${origin}/install/): the one-line installer, the by-hand version, and from source. - [Playground](${origin}/playground/): the tour, ${tourSections.reduce((n, x) => n + x.steps.length, 0)} short programs that run in the browser. - [Playground evidence](${origin}/playground/evidence/): what ran, what checked it. diff --git a/src/pages/reference/index.astro b/src/pages/reference/index.astro new file mode 100644 index 0000000..3b9e153 --- /dev/null +++ b/src/pages/reference/index.astro @@ -0,0 +1,108 @@ +--- +/** + * The reference's index: the prelude's types, then the standard library's + * modules, each with the first sentence its own comment opens with. + */ +import { Image } from "astro:assets"; +import Page from "../../layouts/Page.astro"; +import mascot from "../../assets/mascot.png"; +import { moduleHref, reference, typeHref } from "../../lib/reference"; + +const { modules, prelude, release } = reference; +--- + + +
+
+

+ + Reference, {release.version} +

+

The standard library

+

+ Every type the prelude gives a program and every module under{" "} + std/, with each signature and doc comment exactly as{" "} + iyi doc prints it for {release.tag}. +

+

+ Recorded with {reference.recorded.compiler.split(" (")[0]} at{" "} + {reference.recorded.commit.slice(0, 9)}, and checked on every build + against the source that release ships. +

+
+ +
+
+

Prelude

+

In every program, with no import.

+
+
    + { + prelude.map((page) => ( +
  • + + {page.name} + + {page.summary && {page.summary}} +
  • + )) + } +
+
+ +
+
+

Modules

+

+ Opt in with import std/.... +

+
+
    + { + modules.map((page) => ( +
  • + + {page.name} + + {page.summary && {page.summary}} +
  • + )) + } +
+
+
+ + +
diff --git a/src/pages/reference/prelude/[type].astro b/src/pages/reference/prelude/[type].astro new file mode 100644 index 0000000..1f35f70 --- /dev/null +++ b/src/pages/reference/prelude/[type].astro @@ -0,0 +1,55 @@ +--- +/** + * One prelude type: what every iyi program has without an import, as + * `iyi doc ` printed it at the release. + */ +import { Image } from "astro:assets"; +import Page from "../../../layouts/Page.astro"; +import ReferenceBody from "../../../components/ReferenceBody.astro"; +import mascot from "../../../assets/mascot.png"; +import { reference, referenceHref, typeSearch, type ReferencePage } from "../../../lib/reference"; + +export function getStaticPaths() { + return reference.prelude.map((page) => ({ params: { type: page.slug }, props: { page } })); +} + +const { page } = Astro.props as { page: ReferencePage }; +const search = typeSearch(page); +const lead = page.intro.find((block) => block.kind === "paragraph"); +--- + + +
+
+

+ + Prelude, {reference.release.version} +

+

{page.name}

+ {lead && lead.kind === "paragraph" &&

} +

In the prelude: every iyi program has it, with no import.

+
+ +
+ + +
diff --git a/src/pages/reference/std/[module].astro b/src/pages/reference/std/[module].astro new file mode 100644 index 0000000..21a3f8b --- /dev/null +++ b/src/pages/reference/std/[module].astro @@ -0,0 +1,60 @@ +--- +/** + * One standard library module: its header comment out of the source at the + * release, and its exported surface as `iyi doc` printed it there. + */ +import { Image } from "astro:assets"; +import Page from "../../../layouts/Page.astro"; +import ReferenceBody from "../../../components/ReferenceBody.astro"; +import mascot from "../../../assets/mascot.png"; +import { moduleSearch, reference, referenceHref, type ModulePage } from "../../../lib/reference"; + +export function getStaticPaths() { + return reference.modules.map((page) => ({ params: { module: page.slug }, props: { page } })); +} + +const { page } = Astro.props as { page: ModulePage }; +const search = moduleSearch(page); +const lead = page.intro.find((block) => block.kind === "paragraph"); +const source = `https://github.com/iyilang/iyi/blob/${reference.release.tag}/${page.path}`; +--- + + +
+
+

+ + Standard library, {reference.release.version} +

+

{page.name}

+ {lead && lead.kind === "paragraph" &&

} +

+ import {page.name} to use it. Source:{" "} + {page.path} at {reference.release.tag}. +

+
+ +
+ + +
From c0e0da00a2e25374c2a90eec3ffadd64573af73b Mon Sep 17 00:00:00 2001 From: Serdar Dogruyol <990485+sdogruyol@users.noreply.github.com> Date: Wed, 21 Oct 2026 17:00:59 +0300 Subject: [PATCH 2/2] say what the site is about where a results page reads it The home page's title and heading say programming language; iyilang is the second name, in the colophon and in the structured data. Every description fits the 155 characters a results page shows and every title its 60, and the layout fails the build on either. Lessons carry a searchTitle and a description; playground pages lead with their topic, out of src/playground/topics.json. The notice and the code of conduct are noindex and out of the sitemap, and their titles are no longer Crystal's heading. Reference joins the masthead. --- astro.config.mjs | 17 ++++- doc/ART-DIRECTION.md | 7 +- scripts/project.mjs | 20 ++++- src/content.config.ts | 17 +++++ src/content/learn/declarations-not-bodies.mdx | 2 + src/content/learn/derive-macros.mdx | 2 + src/content/learn/errors-are-values.mdx | 2 + .../learn/generics-across-a-boundary.mdx | 2 + src/content/learn/import.mdx | 2 + src/content/learn/traits-and-impl.mdx | 2 + src/content/learn/typed-at-definition.mdx | 2 + src/content/learn/written-types.mdx | 2 + src/layouts/Page.astro | 71 ++++++++++++++---- src/pages/agents.astro | 2 +- src/pages/download.astro | 2 +- src/pages/index.astro | 8 +- src/pages/install.astro | 2 +- src/pages/learn/[...slug].astro | 4 +- src/pages/learn/index.astro | 2 +- src/pages/playground/[...sample].astro | 12 +-- src/pages/playground/evidence.astro | 2 +- src/pages/project/[doc].astro | 10 ++- src/pages/site.webmanifest.ts | 2 +- src/pages/targets.astro | 2 +- src/pages/why.astro | 2 +- src/playground/samples.ts | 60 +++++++++++++++ src/playground/topics.json | 75 +++++++++++++++++++ 27 files changed, 287 insertions(+), 46 deletions(-) create mode 100644 src/playground/topics.json diff --git a/astro.config.mjs b/astro.config.mjs index 7544ab7..3a0d600 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -2,6 +2,7 @@ import { defineConfig } from "astro/config"; import mdx from "@astrojs/mdx"; import sitemap from "@astrojs/sitemap"; import { unified } from "@astrojs/markdown-remark"; +import { readFileSync } from "node:fs"; // GitHub Pages serves this repository at `iyi.dev` (the CNAME under public/, // which the build copies into dist/ - so the domain is this file's and a @@ -10,12 +11,26 @@ import { unified } from "@astrojs/markdown-remark"; const site = process.env.SITE_ORIGIN ?? "https://iyi.dev"; const base = process.env.SITE_BASE ?? "/"; +// The project documents scripts/project.mjs marks as not this project's to +// rank for. Their pages carry a robots noindex, and a sitemap that listed them +// would be asking a crawler to fetch a page in order to be told to drop it. +// Read from the generated record, which `npm run generate` writes before any +// build, so the list is the script's and is not typed a second time here. +const project = JSON.parse( + readFileSync(new URL("./src/generated/project.json", import.meta.url), "utf8"), +); +const unlisted = new Set( + project.documents + .filter((document) => !document.indexed) + .map((document) => new URL(`${base.replace(/\/$/, "")}/project/${document.id}/`, site).href), +); + export default defineConfig({ site, base, output: "static", trailingSlash: "always", - integrations: [mdx(), sitemap()], + integrations: [mdx(), sitemap({ filter: (page) => !unlisted.has(page) })], // A lesson's URL is its file name, and a published URL outlives a rename: // the old one answers with a page that sends the reader to the new one. diff --git a/doc/ART-DIRECTION.md b/doc/ART-DIRECTION.md index 5e22527..ae50877 100644 --- a/doc/ART-DIRECTION.md +++ b/doc/ART-DIRECTION.md @@ -7,8 +7,11 @@ the other way, the reason it did not is written down. Rendered proof is in ## The concept **A plain, friendly site with a mascot, and every number on it checkable.** -The one line above the fold says what iyi is for: a friendly, fast language for -people and their agents. Beside it stands the mascot, a red heart with an `i` +The one line above the fold says what iyi is for: a friendly, fast programming +language for people and their agents. "Programming language" is in it on +purpose: it is the category a reader searches for, and a line that leaves it +out leaves a search engine guessing what the site is about. Beside it stands +the mascot, a red heart with an `i` on each cheek, waving. Under it a real program, read out of the repository at build time, and the bytes the compiler printed when it ran that program. The site's job is to be readable in one pass by someone who has never heard of iyi, diff --git a/scripts/project.mjs b/scripts/project.mjs index 59c458b..cfb0fc4 100755 --- a/scripts/project.mjs +++ b/scripts/project.mjs @@ -43,11 +43,23 @@ const die = (message) => { // report a hole, how people are expected to behave, whose code this is. // `blurb` is the one sentence of ours on the index, and it says what the // document is, never what it says. +// +// `name` is the page's title in a results list and a browser tab, which the +// document's own first heading is a poor one for: NOTICE.md opens with +// "Crystal Programming Language", the right heading for a notice about +// Crystal's code and the wrong title for a page on this site. The heading +// stays the page's heading; the document is published whole. +// +// `indexed` is false for the two documents whose text is not this project's +// to rank for: the Contributor Covenant is published on thousands of sites +// word for word, and the notice is Crystal's copyright and a list of other +// people's libraries. Both stay published and linked; neither is put forward +// as a search result, and astro.config.mjs keeps them out of the sitemap. const DOCUMENTS = [ - { id: "contributing", file: "CONTRIBUTING.md", blurb: "What a change to this repository is expected to come with." }, - { id: "security", file: "SECURITY.md", blurb: "Where a vulnerability goes, and which of the two trackers is the right one." }, - { id: "conduct", file: "CODE_OF_CONDUCT.md", blurb: "The Contributor Covenant, and the address that enforces it here." }, - { id: "notice", file: "NOTICE.md", blurb: "Crystal's copyright, the license, and every library the compiler links." }, + { id: "contributing", file: "CONTRIBUTING.md", name: "Contributing to iyi", indexed: true, blurb: "What a change to this repository is expected to come with." }, + { id: "security", file: "SECURITY.md", name: "Reporting a security vulnerability in iyi", indexed: true, blurb: "Where a vulnerability goes, and which of the two trackers is the right one." }, + { id: "conduct", file: "CODE_OF_CONDUCT.md", name: "iyi code of conduct", indexed: false, blurb: "The Contributor Covenant, and the address that enforces it here." }, + { id: "notice", file: "NOTICE.md", name: "iyi license and notices", indexed: false, blurb: "Crystal's copyright, the license, and every library the compiler links." }, ]; const commit = execFileSync("git", ["-C", repo, "rev-parse", "HEAD"], { diff --git a/src/content.config.ts b/src/content.config.ts index abc9744..1ab91c4 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -33,8 +33,25 @@ const learn = defineCollection({ loader: glob({ base: "./src/content/learn", pattern: "**/*.{md,mdx}" }), schema: z.object({ title: z.string().min(1), + // The title a results page and a browser tab show, and the words a reader + // would type to find the step: the topic, then the language, then the + // lesson's own title if it fits. `title` is a lesson's heading and it is + // written to be read after the reader arrived ("Errors you cannot ignore"), + // which no one searches for. Sixty characters is about what a results page + // shows before it cuts, and the language's name has to be in it. + searchTitle: z + .string() + .max(60) + .regex(/\biyi\b/, "searchTitle names the language, as in `Error handling in iyi: ...`"), order: z.number().int().positive(), summary: z.string().min(1), + // What a results page prints under the title: the summary, said in the + // space a results page gives it. Plain text, because no search engine + // renders the backticks the summary uses for code. + description: z + .string() + .max(155) + .regex(/^[^`]*$/, "description is plain text: no backticks"), rule: z .string() .regex( diff --git a/src/content/learn/declarations-not-bodies.mdx b/src/content/learn/declarations-not-bodies.mdx index c660cd6..4caaf22 100644 --- a/src/content/learn/declarations-not-bodies.mdx +++ b/src/content/learn/declarations-not-bodies.mdx @@ -1,7 +1,9 @@ --- title: Delete the library, build again +searchTitle: "Separate compilation in iyi: delete the library, build again" order: 1 summary: A module is the unit of compilation, and a consumer compiles against what its imports declare rather than against what they do. Build once keeping each module's artifact, delete the library's source, and build again. +description: "In iyi a module compiles against what its imports declare, not what they do. Build once, delete the library's source, and build again." rule: R-1 sample: samples/iyi/modules.iyi breaks: r1-import-cycle diff --git a/src/content/learn/derive-macros.mdx b/src/content/learn/derive-macros.mdx index 9d0b9a8..ee3da54 100644 --- a/src/content/learn/derive-macros.mdx +++ b/src/content/learn/derive-macros.mdx @@ -1,7 +1,9 @@ --- title: A macro that can only see one declaration +searchTitle: "Derive macros in iyi: a macro that sees one declaration" order: 7 summary: A macro is attached to a declaration with `derive`, it reads that declaration and what its field types implement, and it sees nothing global. So it runs once, in the module that declares the type, and what it generated travels in the artifact. +description: "An iyi macro attaches to one declaration with derive and sees nothing global, so it runs once, where the type is declared, and its output travels with it." rule: R-5 sample: samples/iyi/derive.iyi breaks: r5-global-question diff --git a/src/content/learn/errors-are-values.mdx b/src/content/learn/errors-are-values.mdx index 29a0844..1918581 100644 --- a/src/content/learn/errors-are-values.mdx +++ b/src/content/learn/errors-are-values.mdx @@ -1,7 +1,9 @@ --- title: Errors you cannot ignore +searchTitle: "Error handling in iyi: errors you cannot ignore" order: 8 summary: A function that can fail returns a union. `!` propagates an error member and narrows the rest, an exhaustive case is already total, and adding an error to a signature stops every caller compiling. +description: "In iyi a function that can fail returns a union: ! propagates the error, case must be exhaustive, and adding an error stops every caller compiling." rule: III.1 sample: samples/iyi/errors.iyi breaks: errors-non-exhaustive diff --git a/src/content/learn/generics-across-a-boundary.mdx b/src/content/learn/generics-across-a-boundary.mdx index 41cdfed..9bdfff5 100644 --- a/src/content/learn/generics-across-a-boundary.mdx +++ b/src/content/learn/generics-across-a-boundary.mdx @@ -1,7 +1,9 @@ --- title: A generic that crosses a boundary +searchTitle: "Generics in iyi: a generic that crosses a module boundary" order: 6 summary: Inside a module a generic monomorphizes. Crossing a module boundary it is supposed to pass a dictionary keyed on GC shape, and that half of the rule has nothing behind it yet, so the artifact carries the body and the consumer compiles it again. +description: "Inside a module an iyi generic monomorphizes. Across a module boundary the artifact carries the body today, and the consumer compiles it again." rule: R-4 sample: samples/iyi/immutable.iyi breaks: r4-monomorphize-unknown diff --git a/src/content/learn/import.mdx b/src/content/learn/import.mdx index a4d2d6a..6fa0e4d 100644 --- a/src/content/learn/import.mdx +++ b/src/content/learn/import.mdx @@ -1,7 +1,9 @@ --- title: One keyword, and your own namespace +searchTitle: "Imports in iyi: one keyword, and your own namespace" order: 3 summary: "`import` loads a module and gives you its qualified name. The list or the glob written after the path, `::{...}` or `::*`, is what shortens that, and you write it, so no library can change what a name in your file means by adding an export of its own." +description: "In iyi, import loads a module under its qualified name. The list or glob you write after it shortens names, so no library can change what yours mean." rule: R-2b sample: samples/iyi/modules.iyi breaks: r2b-name-not-imported diff --git a/src/content/learn/traits-and-impl.mdx b/src/content/learn/traits-and-impl.mdx index 6b53391..88c06b4 100644 --- a/src/content/learn/traits-and-impl.mdx +++ b/src/content/learn/traits-and-impl.mdx @@ -1,7 +1,9 @@ --- title: Traits, and the end of open classes +searchTitle: "Traits in iyi: impls, the orphan rule, no open classes" order: 4 summary: A trait names requirements and an impl satisfies them. The orphan rule decides where that impl is allowed to live, and `forall` binds the parameters an impl or a method quantifies over. +description: "An iyi trait names requirements and an impl satisfies them. The orphan rule decides where an impl may live, and forall binds its type parameters." rule: R-3 sample: samples/iyi/hello.iyi breaks: r3-orphan-impl diff --git a/src/content/learn/typed-at-definition.mdx b/src/content/learn/typed-at-definition.mdx index 6568c9c..8666ec2 100644 --- a/src/content/learn/typed-at-definition.mdx +++ b/src/content/learn/typed-at-definition.mdx @@ -1,7 +1,9 @@ --- title: Typed where it is written +searchTitle: "Type checking in iyi: typed where it is written" order: 5 summary: A def that writes all of its types is typed at its definition, caller or no caller. A trait-restricted parameter is typed against exactly its bound, so a generic body cannot use what it did not declare. +description: "An iyi def that writes all its types is typed at its definition, caller or no caller, and a trait-bound parameter is typed against exactly its bound." rule: R-2c sample: samples/iyi/collections.iyi breaks: r2c-outside-bound diff --git a/src/content/learn/written-types.mdx b/src/content/learn/written-types.mdx index e6795ec..05ad06f 100644 --- a/src/content/learn/written-types.mdx +++ b/src/content/learn/written-types.mdx @@ -1,7 +1,9 @@ --- title: What a module writes down +searchTitle: "Typed exports in iyi: what a module writes down" order: 2 summary: If a consumer compiles against declarations, the declarations have to be complete. So an exported name carries full parameter and return types, and everything inside the module goes on inferring. +description: "An exported iyi name writes down its full parameter and return types, so consumers compile against declarations. Inside a module, types are inferred." rule: R-2 sample: samples/iyi/calc.iyi breaks: r2-missing-return-type diff --git a/src/layouts/Page.astro b/src/layouts/Page.astro index 9b1f5c7..8074508 100644 --- a/src/layouts/Page.astro +++ b/src/layouts/Page.astro @@ -66,13 +66,15 @@ interface Props { /** * Whether this page is a URL a crawler should keep. * - * True for every route the build emits. False for exactly one page, the 404: - * Astro writes it to `dist/404.html` and GitHub Pages serves it in place of - * any path that does not exist, so `/404/` is not a route this site has. A - * self-canonical there points a crawler at a URL that is itself a missing - * path, and asks it to index the result. When false this layout emits no - * canonical, no `og:url` and a `robots` meta, because the page is real and - * worth reading and simply is not an address. + * True for every route unless it says otherwise. False for the 404: Astro + * writes it to `dist/404.html` and GitHub Pages serves it in place of any + * path that does not exist, so `/404/` is not a route this site has, and a + * self-canonical there points a crawler at a missing path. False too for a + * published document that is not this project's to rank for, which + * scripts/project.mjs marks. When false this layout emits no canonical, no + * `og:url` and a `robots` noindex, because a canonical beside a noindex is + * two contradicting instructions; astro.config.mjs keeps such a page out of + * the sitemap. */ indexable?: boolean; } @@ -83,10 +85,46 @@ const href = (p: string) => `${base.replace(/\/$/, "")}/${p.replace(/^\//, "")}` const here = Astro.url.pathname; /* One title pattern for the whole site: the page, then the site, unless the - * page is the site. */ + * title already names it. "Install iyi | iyi" spent six of the characters a + * results page shows on saying the name twice, and a title that leads with + * what a reader searched for ("Arrays in iyi") has already said whose page it + * is. */ const siteName = "iyi"; -const isHome = here === base || here === href("/"); -const fullTitle = isHome || title.endsWith(`| ${siteName}`) ? title : `${title} | ${siteName}`; +const fullTitle = /\biyi\b/.test(title) ? title : `${title} | ${siteName}`; + +/* And it has to fit. A results page shows about sixty characters of a title + * and an ellipsis after them, so the words past that are words nobody sees. */ +const TITLE_MAX = 60; +if (fullTitle.length > TITLE_MAX) { + throw new Error( + `Page: the title "${fullTitle}" is ${fullTitle.length} characters, over the ` + + `${TITLE_MAX} a results page shows before it cuts it.`, + ); +} + +/* The names people search for the language by. "iyi" alone is a common + * Turkish word and the name of a political party, so a search for it is never + * about this site; the GitHub organisation is `iyilang`, and that is the one + * second name the copy uses. Search engines read the rest out of the + * structured data below. */ +const alias = "iyilang"; +const alternateNames = [alias, "iyi lang", "iyi programming language"]; + +/* A description is what a results page prints under the title, and it is cut + * at about 155 characters, mid-word, wherever the sentence happens to be. So a + * longer one is not a fuller description, it is a truncated one, and a page + * that is meant to be found has to say what it is in the space it is given. + * Checked here because every page's description passes through here. */ +const DESCRIPTION_MAX = 155; +if (indexable && !summary) { + throw new Error(`Page: "${title}" is indexable and has no summary, so it has no description.`); +} +if (summary && summary.length > DESCRIPTION_MAX) { + throw new Error( + `Page: the summary of "${title}" is ${summary.length} characters, over the ` + + `${DESCRIPTION_MAX} a results page shows before it cuts the sentence:\n ${summary}`, + ); +} const canonical = new URL(here, Astro.site).href; const ogImage = new URL(href("og.png"), Astro.site).href; @@ -101,6 +139,7 @@ const ld = { "@id": `${Astro.site}#website`, url: Astro.site, name: siteName, + alternateName: alternateNames, description: "iyi is a friendly, fast programming language for people and their agents, compatible with Crystal.", inLanguage: "en", @@ -109,6 +148,7 @@ const ld = { "@type": "SoftwareSourceCode", "@id": `${Astro.site}#iyi`, name: "iyi", + alternateName: alternateNames, description: "A compiled programming language whose modules compile against their dependencies' declarations, never their bodies. Compatible with Crystal: the same compiler builds .cr files, and iyi build --crystal gives a program Crystal's standard library and shards.", programmingLanguage: "iyi", @@ -138,6 +178,7 @@ const ld = { const nav = [ ["Why iyi", "why/"], ["Learn", "learn/"], + ["Reference", "reference/"], ["Agents", "agents/"], ["Playground", "playground/"], ["Install", "install/"], @@ -353,11 +394,11 @@ const current = (path: string) => here.startsWith(href(path)); scrolled to the bottom. The licence is one link away, at `/project/notice/`, rather than a second copy here. */}

- iyi is Turkish for good. A fast, compiled - language for developers and the agents working beside them, built - on a single refusal: a module compiles against what its - dependencies declare, never against what they do. Quick builds - and small binaries follow from it. + iyi is Turkish for good. iyi, or {alias}, is a + fast, compiled programming language for developers and the agents + working beside them, built on a single refusal: a module compiles + against what its dependencies declare, never against what they + do. Quick builds and small binaries follow from it.

Install iyi → diff --git a/src/pages/agents.astro b/src/pages/agents.astro index 636e8d3..ebb3020 100644 --- a/src/pages/agents.astro +++ b/src/pages/agents.astro @@ -185,7 +185,7 @@ const diffs = [
diff --git a/src/pages/download.astro b/src/pages/download.astro index d504d33..9425719 100644 --- a/src/pages/download.astro +++ b/src/pages/download.astro @@ -51,7 +51,7 @@ const machineOf = (target: string) =>

Download, version {release.version}

diff --git a/src/pages/index.astro b/src/pages/index.astro index 4a9a3e5..e0621fe 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -70,15 +70,15 @@ const windows = windowsAsset(release); ---
-

A friendly, fast language for people and their agents.

+

A friendly, fast programming language for people and their agents.

- iyi is a compiled language written for two readers: the person editing + iyi is a compiled programming language written for two readers: the person editing the code and the agent editing it beside them. One rule serves both. A module builds against its dependencies' declarations, never their bodies, so: diff --git a/src/pages/install.astro b/src/pages/install.astro index c36090d..e4d3951 100644 --- a/src/pages/install.astro +++ b/src/pages/install.astro @@ -35,7 +35,7 @@ const tarballs = assets.map((asset) => asset.target).join(" or ");

diff --git a/src/pages/learn/[...slug].astro b/src/pages/learn/[...slug].astro index 7116bb5..46beb1f 100644 --- a/src/pages/learn/[...slug].astro +++ b/src/pages/learn/[...slug].astro @@ -98,8 +98,8 @@ const steps = (await sequence()).map((step) => ({
diff --git a/src/pages/learn/index.astro b/src/pages/learn/index.astro index 7e50a51..40a3841 100644 --- a/src/pages/learn/index.astro +++ b/src/pages/learn/index.astro @@ -152,7 +152,7 @@ const further = [
diff --git a/src/pages/playground/[...sample].astro b/src/pages/playground/[...sample].astro index 198fc20..cb7ab7b 100644 --- a/src/pages/playground/[...sample].astro +++ b/src/pages/playground/[...sample].astro @@ -37,7 +37,7 @@ import Page from "../../layouts/Page.astro"; import Playground from "../../components/Playground.astro"; import type { TourStep } from "../../playground/samples"; import type { SampleEntry } from "../../playground/entry"; -import { curatedSamples, tourSamples } from "../../playground/samples"; +import { curatedSamples, searchOf, tourSamples } from "../../playground/samples"; /* Eager because this runs in node during the static build and every route * needs one of these. Astro never sends a frontmatter import to a browser, so @@ -67,7 +67,7 @@ interface Props { } const { sample } = Astro.props as Props; -const runs = "wasm" in sample; +const search = searchOf(sample); /* The slice for this sample. Missing is fatal rather than silently absent: a * page rendered without it would carry an editor that cannot tell whether the @@ -85,12 +85,8 @@ if (entry === undefined) { --- diff --git a/src/pages/playground/evidence.astro b/src/pages/playground/evidence.astro index 95a3bc0..e567215 100644 --- a/src/pages/playground/evidence.astro +++ b/src/pages/playground/evidence.astro @@ -232,7 +232,7 @@ const marked = diagnosticEvents.map((event) => {
diff --git a/src/pages/project/[doc].astro b/src/pages/project/[doc].astro index b8490de..0c2f54d 100644 --- a/src/pages/project/[doc].astro +++ b/src/pages/project/[doc].astro @@ -32,7 +32,12 @@ interface Block { interface Document { id: string; file: string; + /** The document's own first heading: the page's h1. */ title: string; + /** The page's title, from scripts/project.mjs, which says why it differs. */ + name: string; + /** Whether a search engine is asked to index the page. */ + indexed: boolean; blurb: string; lines: number; cite: string; @@ -60,8 +65,9 @@ const others = project.documents.filter((entry) => entry.id !== doc.id);
diff --git a/src/pages/site.webmanifest.ts b/src/pages/site.webmanifest.ts index 896a93a..b16467c 100644 --- a/src/pages/site.webmanifest.ts +++ b/src/pages/site.webmanifest.ts @@ -41,7 +41,7 @@ export const GET = () => { { name: "iyi", short_name: "iyi", - description: "A friendly, fast language for people and their agents.", + description: "A friendly, fast programming language for people and their agents.", start_url: "/", display: "browser", background_color: paper, diff --git a/src/pages/targets.astro b/src/pages/targets.astro index 0061c24..f47ff00 100644 --- a/src/pages/targets.astro +++ b/src/pages/targets.astro @@ -94,7 +94,7 @@ const otherSpelling = audit.values.filter(

Platforms

diff --git a/src/pages/why.astro b/src/pages/why.astro index e98bb66..83cfe6c 100644 --- a/src/pages/why.astro +++ b/src/pages/why.astro @@ -76,7 +76,7 @@ const shards = ["kemal", "db", "ameba", "sqlite3"];
diff --git a/src/playground/samples.ts b/src/playground/samples.ts index 67ab384..62f33e7 100644 --- a/src/playground/samples.ts +++ b/src/playground/samples.ts @@ -44,6 +44,7 @@ import { recordedText } from "../lib/highlight"; import manifest from "../../records/wasm/manifest.json"; import toc from "../../samples/tour/tour.json"; +import topics from "./topics.json"; /** * The machine stamp every record carries. Identical in shape across all three @@ -289,3 +290,62 @@ export function sampleNote(pathOrId: string): string | null { export function blurbOf(path: string): string { return recordedText(path).split("\n")[0].replace(/^#\s*/, "").replace(/\.$/, ""); } + +type Topic = { topic: string; about?: string }; +const topicOf: Record = topics.samples; + +/** + * What a search engine is told a program's page is: a title that leads with + * the topic a reader searched for, and a description that says what the + * program does and that it runs here. + * + * The topic is ours, out of `topics.json`, because a sample's id (`nil`, + * `iyi/std_time`) is a file name and not something anyone types into a search + * box. The first sentence of the description is the program's own first + * comment line where that line is a sentence, which for the tour it always + * is, and the entry's `about` for a repository sample, whose first line is + * written for a reader of the repository. Both directions are checked: a page + * with no entry, or a repository sample with no `about`, fails the build. + */ +export function searchOf(step: TourStep): { title: string; description: string } { + const entry = topicOf[step.id]; + if (entry === undefined) { + throw new Error( + `playground: src/playground/topics.json has no entry for "${step.id}", ` + + `so its page has no title a reader would search for. Add one.`, + ); + } + if (step.set === "iyi" && entry.about === undefined) { + throw new Error( + `playground: src/playground/topics.json gives "${step.id}" no \`about\`. ` + + `A repository sample's first comment line is written for the repository, ` + + `not for a results page, so its description is written here.`, + ); + } + const sentence = (entry.about ?? blurbOf(step.path)).replace(/`/g, "").replace(/\.$/, ""); + return runnable(step) + ? { + title: `${entry.topic} in iyi: a runnable example`, + description: `${sentence}. Run it in your browser, compiled to WebAssembly by iyi.`, + } + : { + title: `${entry.topic} in iyi: an example and its output`, + description: `${sentence}. An iyi example, with what it printed when compiled natively.`, + }; +} + +/* The other direction: an entry for a program that has no page is a topic + * nobody reads, and usually the trace of a rename the map did not follow. */ +{ + const paged = new Set([ + ...tourSamples.map((step) => step.id), + ...curatedSamples.filter((sample) => sample.set === "iyi").map((sample) => sample.id), + ]); + const stale = Object.keys(topicOf).filter((id) => !paged.has(id)); + if (stale.length > 0) { + throw new Error( + `playground: src/playground/topics.json names ${stale.join(", ")}, which ` + + `no playground page carries. Remove the entries or the rename that orphaned them.`, + ); + } +} diff --git a/src/playground/topics.json b/src/playground/topics.json new file mode 100644 index 0000000..5d63c25 --- /dev/null +++ b/src/playground/topics.json @@ -0,0 +1,75 @@ +{ + "note": [ + "What each playground page is about, in the words a reader would search for.", + "`topic` leads the page's title, as in \"Arrays in iyi: a runnable example\".", + "`about` is the first sentence of the page's description. A tour program's", + "own first comment line is that sentence already, so a tour entry has none;", + "a repository sample opens with a line written for someone reading the", + "repository (\"The first program that forgets - a table of...\"), cut", + "mid-sentence or ending on a SPEC.md section, so every `iyi/` entry carries", + "one. src/playground/samples.ts refuses a page with no entry, and an entry", + "with no page." + ], + "samples": { + "hello": { "topic": "Hello world" }, + "modules": { "topic": "Modules" }, + "exports": { "topic": "Importing names" }, + "variables": { "topic": "Variables" }, + "types": { "topic": "Basic types" }, + "conversions": { "topic": "Type conversions" }, + "constants": { "topic": "Constants" }, + "functions": { "topic": "Functions" }, + "returns": { "topic": "Multiple return values" }, + "nil": { "topic": "Nil and nilable types" }, + "loops": { "topic": "Loops" }, + "ranges": { "topic": "Ranges" }, + "conditions": { "topic": "If, elsif and unless" }, + "case": { "topic": "Case expressions" }, + "defer": { "topic": "Defer" }, + "structs": { "topic": "Structs" }, + "classes": { "topic": "Classes" }, + "arrays": { "topic": "Arrays" }, + "hashes": { "topic": "Hashes" }, + "tuples": { "topic": "Tuples" }, + "blocks": { "topic": "Blocks and closures" }, + "methods": { "topic": "Methods" }, + "traits": { "topic": "Traits" }, + "dispatch": { "topic": "Dynamic dispatch" }, + "defaults": { "topic": "Default trait methods" }, + "unions": { "topic": "Union types" }, + "errors": { "topic": "Errors as values" }, + "strings": { "topic": "Strings and to_s" }, + "generic-functions": { "topic": "Generic functions" }, + "generic-types": { "topic": "Generic types" }, + "bounds": { "topic": "Trait bounds" }, + "tasks": { "topic": "Tasks and concurrency" }, + "channels": { "topic": "Channels" }, + + "iyi/basics": { "topic": "First programs", "about": "The first half hour of iyi: what a person writes before they write anything real" }, + "iyi/calc": { "topic": "A calculator language", "about": "A calculator language with variables, written in iyi: the biggest of the sample programs" }, + "iyi/collections": { "topic": "Enumerable", "about": "The Enumerable trait from the standard library, implemented twice, with every method called" }, + "iyi/config": { "topic": "Parsing a config file", "about": "Reading a key = value config file, with the line number in its error" }, + "iyi/derive": { "topic": "Derive macros", "about": "A derive macro, and where it runs: once, in the module that declares the type" }, + "iyi/enums": { "topic": "Enums", "about": "An enum used the way a program uses one: its values, comparing members, printing them" }, + "iyi/errors": { "topic": "Error handling", "about": "Errors are ordinary union members: no Result wrapper and no exception hierarchy" }, + "iyi/files": { "topic": "Reading and writing files", "about": "Write a file, read it back, check that it exists, then delete it" }, + "iyi/format": { "topic": "Format strings", "about": "Format strings: width, alignment, padding, precision, and bases" }, + "iyi/formatting": { "topic": "Negative indexing and padding", "about": "Negative indexing, numbers in any base, and padding with rjust, ljust and repeat" }, + "iyi/generics": { "topic": "Traits for generic types", "about": "Implementing a trait for a generic type" }, + "iyi/grid": { "topic": "Tables of cells", "about": "A table of cells built from their coordinates, read across, down and along the diagonal" }, + "iyi/hello": { "topic": "Traits and impls", "about": "The first iyi program: structs, traits and impls in one file, and what is still missing" }, + "iyi/immutable": { "topic": "Immutable lists", "about": "The standard library's shareable immutable sequence, and the evidence that it cannot change" }, + "iyi/init_order": { "topic": "Module initialization order", "about": "Module initialization order: a module initializes after every module it imports" }, + "iyi/inventory": { "topic": "Lists of structs", "about": "A list of structs and the questions a list gets asked: which ones, how many, the largest" }, + "iyi/io": { "topic": "Reading standard input", "about": "Reading standard input one line at a time" }, + "iyi/modules": { "topic": "Modules across files", "about": "Modules across files: import, and the names it brings into scope" }, + "iyi/sessions": { "topic": "Deleting from a hash", "about": "A table of who is signed in, which loses an entry when somebody signs out" }, + "iyi/shapes": { "topic": "A trait of your own", "about": "Two structs behind one trait, held in a list typed by the trait, their areas added up" }, + "iyi/socket": { "topic": "TCP sockets", "about": "A TCP client and server in one program, on loopback" }, + "iyi/std_iterator": { "topic": "Lazy iterators", "about": "Lazy sequences with std/iterator: elements transformed on demand, when pulled" }, + "iyi/std_text": { "topic": "Text processing", "about": "Text processing with the standard library's std/text" }, + "iyi/std_time": { "topic": "Dates and times", "about": "std/time: wall clock, monotonic stopwatch, civil calendar, and RFC 3339" }, + "iyi/visited": { "topic": "Walking a graph", "about": "A walk over a graph with cycles that ends because it keeps a set of where it has been" }, + "iyi/webapp": { "topic": "A web app, Sinatra style", "about": "The Sinatra feel, kept: routes written by the app rather than injected by the library" } + } +}