Skip to content
Topaz6.0 · Before release
Download

Search documentation

Specification 6.0 · Not released · Tested product 5.21.3

Use arrow keys to choose a result. Press Enter to open.

Type an API name, diagnostic number, rule, or project topic.

    Library

    std.bytes

    Construct byte sequences and use byte buffers, encodings, hashes, and compression.

    APIs in this module

    33 APIs

    NameModule
    ByteBuffer.allocatestd.bytes
    ByteBuffer.copystd.bytes
    ByteBuffer.fillstd.bytes
    ByteBuffer.fromBytesstd.bytes
    ByteBuffer.getstd.bytes
    ByteBuffer.lengthstd.bytes
    ByteBuffer.setstd.bytes
    ByteBuffer.toBytesstd.bytes
    Bytes.concatstd.bytes
    Bytes.crc32std.bytes
    Bytes.decodeUtf8std.bytes
    Bytes.deflateCompressstd.bytes
    Bytes.deflateDecompressstd.bytes
    Bytes.deflateFixedCompressstd.bytes
    Bytes.emptystd.bytes
    Bytes.fromArraystd.bytes
    Bytes.fromBase64std.bytes
    Bytes.fromHexstd.bytes
    Bytes.getstd.bytes
    Bytes.gzipCompressstd.bytes
    Bytes.gzipDecompressstd.bytes
    Bytes.hmacSha256std.bytes
    Bytes.lengthstd.bytes
    Bytes.reedSolomon255223Protectstd.bytes
    Bytes.sha256std.bytes
    Bytes.sha512std.bytes
    Bytes.slicestd.bytes
    Bytes.toArraystd.bytes
    Bytes.toBase64std.bytes
    Bytes.toHexstd.bytes
    Bytes.zlibFixedCompressstd.bytes
    Bytes.zstdCompressstd.bytes
    Bytes.zstdDecompressstd.bytes

    Module

    Link

    Bytes stores immutable octets. ByteBuffer is a mutable value with a fixed length. Indexing counts bytes and text conversion uses an explicit encoding.

    Why

    Link

    Binary data needs exact byte values, explicit text encodings, and a mutable buffer for incremental work.

    Availability

    Link

    Import std.bytes in language mode 5.21. The current compiler selects std/bytes.tpz for this language mode. Availability in other released editions and inclusion in each installed distribution are Pending.

    Exported types

    Link

    export type Bytes = Bytes

    export type ByteBuffer = ByteBuffer

    Type members keep the compiler-owned type name shown in their declarations. Importing a module does not create a new type identity.

    Public API

    Link

    ByteBuffer.allocate

    ByteBuffer.copy

    ByteBuffer.fill

    ByteBuffer.fromBytes

    ByteBuffer.get

    ByteBuffer.length

    ByteBuffer.set

    ByteBuffer.toBytes

    Bytes.concat

    Bytes.crc32

    Bytes.decodeUtf8

    Bytes.deflateCompress

    Bytes.deflateDecompress

    Bytes.deflateFixedCompress

    Bytes.empty

    Bytes.fromArray

    Bytes.fromBase64

    Bytes.fromHex

    Bytes.get

    Bytes.gzipCompress

    Bytes.gzipDecompress

    Bytes.hmacSha256

    Bytes.length

    Bytes.reedSolomon255223Protect

    Bytes.sha256

    Bytes.sha512

    Bytes.slice

    Bytes.toArray

    Bytes.toBase64

    Bytes.toHex

    Bytes.zlibFixedCompress

    Bytes.zstdCompress

    Bytes.zstdDecompress

    Pending contracts

    Link

    Read collection sizes, keys, and values as properties

    Link
    Ruletopaz.stdlib.contract.collection-properties.rule

    Read collection sizes, keys, and values as properties

    Bytes.length and ByteBuffer.length are read-only int properties that report the number of bytes.

    JSONValue.length is an Option<int> property. Arrays and objects return Some(count) for their element or member count. Other JSON kinds return None().

    JSONValue.keys and JSONValue.values are properties returning Option<Array<string>> and Option<Array<JSONValue>>. Objects return Some(keys) or Some(values). Other JSON kinds return None().

    Read these properties without parentheses, as with Array.length, Map.length, Map.keys, Map.values, and Set.length. Calling a property value is rejected as a non-callable value.

    Why

    The same collection observation should use the same syntax across core collections and library values. Keeping JSONValue results optional preserves its distinction between collections and scalar values.

    Source evidence

    Read the same properties on core collections

    Link
    main.tpzRead the same properties on core collectionsOpen in Playground
    1. import std.io
    2. let array = [2, 3]
    3. let entries = map { "name": "Topaz" }
    4. let values = set { 2, 3 }
    5. io.writeLine("{array.length}:{entries.length}:{values.length}")
    6. io.writeLine(entries.keys[0])
    7. io.writeLine(entries.values[0])
    Expected outputexit 0
    2:1:2
    name
    Topaz
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 2:1:2
    2. name
    3. Topaz
    Source evidence

    Read a byte sequence size

    Link
    main.tpzRead a byte sequence sizeOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. let bytes = Bytes.empty()
    4. let size: int = bytes.length
    5. io.writeLine("{size}")
    Expected outputexit 0
    0
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 0
    Source evidence

    Read a byte buffer size

    Link
    main.tpzRead a byte buffer sizeOpen in Playground
    1. import std.bytes { ByteBuffer }
    2. import std.io
    3. let buffer = ByteBuffer.allocate(3)
    4. let size: int = buffer.length
    5. io.writeLine("{size}")
    Expected outputexit 0
    3
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 3
    Source evidence

    Read JSON sizes and preserve optional results

    Link
    main.tpzRead JSON sizes and preserve optional resultsOpen in Playground
    1. import std.json
    2. import std.test
    3. import std.io
    4. let array = test.assertOk(json.parse("[1,2]"))
    5. let object = test.assertOk(json.parse("\{\"name\":\"Topaz\"\}"))
    6. let scalar = test.assertOk(json.parse("42"))
    7. let arraySize: Option<int> = array.length
    8. let objectSize: Option<int> = object.length
    9. let scalarSize: Option<int> = scalar.length
    10. test.assertEq(test.assertSome(arraySize), 2)
    11. test.assertEq(test.assertSome(objectSize), 1)
    12. test.assertNone(scalarSize)
    13. io.writeLine("JSON sizes read")
    Expected outputexit 0
    JSON sizes read
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. JSON sizes read
    Source evidence

    Read JSON object keys

    Link
    main.tpzRead JSON object keysOpen in Playground
    1. import std.json
    2. import std.test
    3. import std.io
    4. let object = test.assertOk(json.parse("\{\"name\":\"Topaz\"\}"))
    5. let array = test.assertOk(json.parse("[]"))
    6. let scalar = test.assertOk(json.parse("42"))
    7. let names: Option<Array<string>> = object.keys
    8. test.assertEq(test.assertSome(names)[0], "name")
    9. test.assertNone(array.keys)
    10. test.assertNone(scalar.keys)
    11. io.writeLine("JSON keys read")
    Expected outputexit 0
    JSON keys read
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. JSON keys read
    Source evidence

    Read JSON object values

    Link
    main.tpzRead JSON object valuesOpen in Playground
    1. import std.json
    2. import std.test
    3. import std.io
    4. let object = test.assertOk(json.parse("\{\"name\":\"Topaz\"\}"))
    5. let array = test.assertOk(json.parse("[]"))
    6. let scalar = test.assertOk(json.parse("42"))
    7. let values = test.assertSome(object.values)
    8. test.assertEq(test.assertSome(values[0].asString()), "Topaz")
    9. test.assertNone(array.values)
    10. test.assertNone(scalar.values)
    11. io.writeLine("JSON values read")
    Expected outputexit 0
    JSON values read
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. JSON values read
    Source evidence

    Reject calling Bytes.length

    Link
    main.tpzReject calling Bytes.lengthOpen in Playground
    1. import std.bytes { Bytes }
    2. function inspect(value: Bytes) -> () {
    3. let result = value.length()
    4. return ()
    5. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check main.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5005 stdlib-property-not-callable at main.tpz:3:22
    Source evidence

    Reject calling ByteBuffer.length

    Link
    main.tpzReject calling ByteBuffer.lengthOpen in Playground
    1. import std.bytes { ByteBuffer }
    2. function inspect(value: ByteBuffer) -> () {
    3. let result = value.length()
    4. return ()
    5. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check main.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5005 stdlib-property-not-callable at main.tpz:3:22
    Source evidence

    Reject calling JSONValue.length

    Link
    main.tpzReject calling JSONValue.lengthOpen in Playground
    1. import std.json { JSONValue }
    2. function inspect(value: JSONValue) -> () {
    3. let result = value.length()
    4. return ()
    5. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check main.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5005 stdlib-property-not-callable at main.tpz:3:22
    Source evidence

    Reject calling JSONValue.keys

    Link
    main.tpzReject calling JSONValue.keysOpen in Playground
    1. import std.json { JSONValue }
    2. function inspect(value: JSONValue) -> () {
    3. let result = value.keys()
    4. return ()
    5. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check main.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5005 stdlib-property-not-callable at main.tpz:3:22
    Source evidence

    Reject calling JSONValue.values

    Link
    main.tpzReject calling JSONValue.valuesOpen in Playground
    1. import std.json { JSONValue }
    2. function inspect(value: JSONValue) -> () {
    3. let result = value.values()
    4. return ()
    5. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check main.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5005 stdlib-property-not-callable at main.tpz:3:22
    Source evidence

    Diagnostics

    Link

    Value is not callable

    Test collection emptiness with length

    Link
    Ruletopaz.stdlib.contract.collection-emptiness.rule

    Test collection emptiness with length

    Test whether a Bytes, Map, or Set value is empty by comparing its length property with zero. The comparison is true exactly when the collection has no bytes, entries, or elements.

    Bytes, Map, and Set do not expose an isEmpty member. Read the length property and use the ordinary equality operator.

    Why

    A collection already exposes its count through length. Comparing that value with zero expresses emptiness for every collection without another query name or a type-specific method.

    Source evidence

    Test empty and nonempty maps with length

    Link
    main.tpzTest empty and nonempty maps with lengthOpen in Playground
    1. import std.io
    2. let empty: Map<string, int> = map {}
    3. let filled = map { "one": 1 }
    4. io.writeLine("{empty.length == 0}:{filled.length == 0}")
    Expected outputexit 0
    true:false
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. true:false
    Source evidence

    Test empty and nonempty sets with length

    Link
    main.tpzTest empty and nonempty sets with lengthOpen in Playground
    1. import std.io
    2. let empty: Set<int> = set {}
    3. let filled = set { 1 }
    4. io.writeLine("{empty.length == 0}:{filled.length == 0}")
    Expected outputexit 0
    true:false
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. true:false
    Source evidence

    Test empty and nonempty byte sequences with length

    Link
    main.tpzTest empty and nonempty byte sequences with lengthOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.test
    3. import std.io
    4. let empty = Bytes.empty()
    5. let filled = test.assertOk(Bytes.fromArray([1]))
    6. io.writeLine("{empty.length == 0}:{filled.length == 0}")
    Expected outputexit 0
    true:false
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. true:false
    Source evidence

    Reject Bytes.isEmpty

    Link
    main.tpzReject Bytes.isEmptyOpen in Playground
    1. import std.bytes { Bytes }
    2. function inspect(value: Bytes) -> () {
    3. let result = value.isEmpty()
    4. return ()
    5. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check main.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5006 stdlib-removed-member at main.tpz:3:22
    Source evidence

    Reject Map.isEmpty

    Link
    main.tpzReject Map.isEmptyOpen in Playground
    1. function inspect(value: Map<string, int>) -> () {
    2. let result = value.isEmpty()
    3. return ()
    4. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check main.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5006 stdlib-removed-member at main.tpz:2:22
    Source evidence

    Reject Set.isEmpty

    Link
    main.tpzReject Set.isEmptyOpen in Playground
    1. function inspect(value: Set<int>) -> () {
    2. let result = value.isEmpty()
    3. return ()
    4. }
    Expected outputNonzero exit
    No stdout.
    $ topaz check main.tpz --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during check. Nonzero exit.

    • TPZ5006 stdlib-removed-member at main.tpz:2:22
    Source evidence

    Diagnostics

    Link

    Unknown member or field

    Construct and inspect immutable byte sequences

    Link
    Ruletopaz.stdlib.contract.bytes-values.rule

    Construct and inspect immutable byte sequences

    A Bytes value is an immutable ordered sequence of octets. Each octet is an int from 0 through 255.

    Bytes.empty returns the empty sequence. Bytes.fromArray accepts an Array<int> and returns an independent snapshot in Ok.

    Bytes.fromArray returns Err with a string identifying the first element outside 0 through 255. It does not wrap, clamp, or return a partial sequence.

    Bytes.toArray returns a new Array<int> in byte order. Mutating that array or the original input array does not change an existing Bytes value.

    Bytes.concat returns the receiver bytes followed by the argument bytes. Either input may be empty and both inputs retain their contents.

    Bytes.get uses a zero-based byte index. It returns Some(byte) within the sequence and None() for every negative or past-end index.

    Bytes.length is the number of bytes and is read as a property. Compare it with zero to test emptiness.

    Receivers and supplied arguments evaluate once from left to right before the operation begins. These operations perform no I/O or text normalization.

    A correctly typed lookup or concatenation has no data-dependent Result failure. Allocation exhaustion is an operational failure and is not a decoded data error.

    Why

    Exact octets keep binary payloads independent of text encoding. Snapshot conversion prevents later array edits from silently changing a payload.

    Source evidence

    Preserve empty values and byte order

    Link
    main.tpzPreserve empty values and byte orderOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. let empty = test.assertOk(Bytes.fromArray([]))
    5. let value = test.assertOk(Bytes.fromArray([0, 128, 255]))
    6. io.writeLine(Bytes.empty().toHex())
    7. io.writeLine(empty.concat(value).toHex())
    8. io.writeLine(value.concat(test.assertOk(Bytes.fromArray([1]))).toHex())
    Expected outputexit 0
    
    0080ff
    0080ff01
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 0080ff
    2. 0080ff01
    Source evidence

    Reject out-of-range array elements

    Link
    main.tpzReject out-of-range array elementsOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. for values in ([[0, -1, 255], [0, 256, 255]]) {
    4. match (Bytes.fromArray(values)) {
    5. case Err(_) => io.writeLine("invalid byte")
    6. case Ok(_) => io.writeLine("accepted")
    7. }
    8. }
    Expected outputexit 0
    invalid byte
    invalid byte
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. invalid byte
    2. invalid byte
    Source evidence

    Read optional bytes at both boundaries

    Link
    main.tpzRead optional bytes at both boundariesOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. let value = test.assertOk(Bytes.fromArray([0, 128, 255]))
    5. for index in ([-1, 0, 2, 3]) {
    6. match (value.get(index)) {
    7. case Some(byte) => io.writeLine("{byte}")
    8. case None() => io.writeLine("absent")
    9. }
    10. }
    Expected outputexit 0
    absent
    0
    255
    absent
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. absent
    2. 0
    3. 255
    4. absent
    Source evidence

    Keep array edits independent from bytes

    Link
    main.tpzKeep array edits independent from bytesOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. let mut input = [0, 255]
    5. let value = test.assertOk(Bytes.fromArray(input))
    6. let mut output = value.toArray()
    7. input[0] = 7
    8. output[1] = 8
    9. io.writeLine(value.toHex())
    10. io.writeLine("{input[0]},{output[1]}")
    Expected outputexit 0
    00ff
    7,8
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 00ff
    2. 7,8
    Source evidence

    Evaluate the receiver before its argument

    Link
    main.tpzEvaluate the receiver before its argumentOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. function receiver() -> Bytes {
    4. io.writeLine("receiver")
    5. return "a".encodeUtf8()
    6. }
    7. function argument() -> Bytes {
    8. io.writeLine("argument")
    9. return "b".encodeUtf8()
    10. }
    11. io.writeLine(receiver().concat(argument()).toHex())
    Expected outputexit 0
    receiver
    argument
    6162
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. receiver
    2. argument
    3. 6162
    Source evidence

    Select explicit byte intervals

    Link
    Ruletopaz.stdlib.contract.bytes-slice.rule

    Select explicit byte intervals

    Bytes.slice uses a start position and an exclusive end position measured in bytes.

    Valid bounds satisfy 0 <= start <= end <= length. The result contains exactly the bytes from start through end minus one.

    Equal endpoints produce empty Bytes, including endpoints at zero and at length. A full interval preserves every byte.

    Negative bounds, inverted bounds, and endpoints beyond length produce TPZ4001 at runtime. Bounds are not clamped or interpreted from the end.

    Slicing never changes the receiver. The receiver, start, and end expressions evaluate once in that order before the bounds are checked.

    A byte interval may split a UTF-8 encoding. Bytes.slice preserves those selected octets and Bytes.decodeUtf8 reports invalid text when they form an incomplete sequence.

    Why

    An explicit interval should identify exactly the requested payload. Rejecting an invalid interval makes a mistaken byte count visible before subsequent decoding.

    Source evidence

    Select empty and nonempty intervals

    Link
    main.tpzSelect empty and nonempty intervalsOpen in Playground
    1. import std.io
    2. let value = "abcd".encodeUtf8()
    3. io.writeLine(value.slice(0, 0).toHex())
    4. io.writeLine(value.slice(1, 3).toHex())
    5. io.writeLine(value.slice(4, 4).toHex())
    6. io.writeLine(value.toHex())
    Expected outputexit 0
    
    6263
    
    61626364
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 6263
    2. 61626364
    Source evidence

    Keep byte slicing separate from UTF-8 validation

    Link
    main.tpzKeep byte slicing separate from UTF-8 validationOpen in Playground
    1. import std.io
    2. let byte = "é".encodeUtf8().slice(0, 1)
    3. io.writeLine(byte.toHex())
    4. match (byte.decodeUtf8()) {
    5. case Err(_) => io.writeLine("invalid UTF-8")
    6. case Ok(_) => io.writeLine("accepted")
    7. }
    Expected outputexit 0
    c3
    invalid UTF-8
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. c3
    2. invalid UTF-8
    Source evidence

    Reject a negative slice start

    Link
    main.tpzReject a negative slice startOpen in Playground
    1. import std.bytes { Bytes }
    2. let value = "abcd".encodeUtf8()
    3. value.slice(-1, 2)
    Expected outputNonzero exit
    No stdout.
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during run. Nonzero exit.

    • TPZ4001 bytes-slice-bounds at main.tpz:3:1
    Source evidence

    Reject an inverted byte interval

    Link
    main.tpzReject an inverted byte intervalOpen in Playground
    1. import std.bytes { Bytes }
    2. let value = "abcd".encodeUtf8()
    3. value.slice(3, 2)
    Expected outputNonzero exit
    No stdout.
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during run. Nonzero exit.

    • TPZ4001 bytes-slice-bounds at main.tpz:3:1
    Source evidence

    Reject an endpoint beyond the sequence

    Link
    main.tpzReject an endpoint beyond the sequenceOpen in Playground
    1. import std.bytes { Bytes }
    2. let value = "abcd".encodeUtf8()
    3. value.slice(0, 5)
    Expected outputNonzero exit
    No stdout.
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during run. Nonzero exit.

    • TPZ4001 bytes-slice-bounds at main.tpz:3:1
    Source evidence

    Diagnostics

    Link

    Index out of bounds

    Use a canonical hexadecimal spelling

    Link
    Ruletopaz.stdlib.contract.bytes-hex.rule

    Use a canonical hexadecimal spelling

    Bytes.toHex emits two lowercase ASCII hexadecimal digits per byte with the high nibble first. Empty Bytes produces an empty string.

    Bytes.fromHex accepts an even number of digits drawn from 0 through 9 and a through f. Empty text returns Ok with empty Bytes.

    Uppercase letters, prefixes, separators, whitespace, non-ASCII characters, and an odd digit count return Err with a string describing invalid hex input.

    Decoding returns the entire byte sequence in Ok or an error with no decoded prefix. It never mutates an input value.

    Encoding then decoding recovers the bytes exactly. Decoding then encoding preserves every accepted input spelling.

    The input expression evaluates once. These conversions perform no I/O and do not apply Unicode normalization.

    Why

    One accepted hexadecimal spelling makes serialized byte text stable for comparison and reproduction. Data that uses another spelling can be normalized explicitly before decoding.

    Source evidence

    Decode byte pairs and emit lowercase text

    Link
    main.tpzDecode byte pairs and emit lowercase textOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. io.writeLine(test.assertOk(Bytes.fromHex("")).toHex())
    5. io.writeLine(test.assertOk(Bytes.fromHex("000f80ff")).toHex())
    Expected outputexit 0
    
    000f80ff
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 000f80ff
    Source evidence

    Reject malformed hexadecimal strings

    Link
    main.tpzReject malformed hexadecimal stringsOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. for text in (["0", "gg", "0x00", "00 11", "00"]) {
    4. match (Bytes.fromHex(text)) {
    5. case Err(_) => io.writeLine("invalid hex")
    6. case Ok(_) => io.writeLine("accepted")
    7. }
    8. }
    Expected outputexit 0
    invalid hex
    invalid hex
    invalid hex
    invalid hex
    invalid hex
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. invalid hex
    2. invalid hex
    3. invalid hex
    4. invalid hex
    5. invalid hex
    Source evidence

    Reject uppercase hexadecimal input

    Link
    main.tpzReject uppercase hexadecimal inputOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. match (Bytes.fromHex("CAfe")) {
    4. case Err(_) => io.writeLine("uppercase rejected")
    5. case Ok(_) => io.writeLine("accepted")
    6. }
    Expected outputexit 0
    uppercase rejected
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. uppercase rejected
    Source evidence

    Encode and validate padded Base64

    Link
    Ruletopaz.stdlib.contract.bytes-base64.rule

    Encode and validate padded Base64

    Bytes.toBase64 uses the RFC 4648 standard alphabet with plus and slash. Its output has no whitespace or line wrapping.

    A final group containing one byte ends with two equals signs. A final group containing two bytes ends with one equals sign.

    Bytes.fromBase64 accepts complete groups of four characters. Padding appears at the end of the final group and all unused bits are zero.

    Missing or misplaced padding, nonzero unused bits, whitespace, non-ASCII characters, minus signs, and underscores return Err with a string.

    Empty bytes encode as empty text and empty text decodes to Ok with empty Bytes. Invalid text never returns a partial byte sequence.

    The input is evaluated once and is unchanged. Encoding then decoding recovers the input bytes exactly without any external effects.

    Why

    A precise alphabet and padding rule let independent implementations exchange the same bytes. Rejecting nonzero unused bits prevents several texts from representing one accepted encoding.

    Source evidence

    Use standard Base64 vectors

    Link
    main.tpzUse standard Base64 vectorsOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. for text in (["", "f", "fo", "foo"]) {
    5. io.writeLine(text.encodeUtf8().toBase64())
    6. }
    7. for text in (["", "Zg==", "Zm8=", "Zm9v"]) {
    8. io.writeLine(test.assertOk(Bytes.fromBase64(text)).toHex())
    9. }
    Expected outputexit 0
    
    Zg==
    Zm8=
    Zm9v
    
    66
    666f
    666f6f
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. Zg==
    2. Zm8=
    3. Zm9v
    4. 66
    5. 666f
    6. 666f6f
    Source evidence

    Reject padding and alphabet errors

    Link
    main.tpzReject padding and alphabet errorsOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. for text in (["Zg", "Zh==", "Zg== ", "_w==", "Zg==Zg==", "=g=="]) {
    4. match (Bytes.fromBase64(text)) {
    5. case Err(_) => io.writeLine("invalid Base64")
    6. case Ok(_) => io.writeLine("accepted")
    7. }
    8. }
    Expected outputexit 0
    invalid Base64
    invalid Base64
    invalid Base64
    invalid Base64
    invalid Base64
    invalid Base64
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. invalid Base64
    2. invalid Base64
    3. invalid Base64
    4. invalid Base64
    5. invalid Base64
    6. invalid Base64
    Source evidence

    Decode UTF-8 without replacing data

    Link
    Ruletopaz.stdlib.contract.bytes-utf8.rule

    Decode UTF-8 without replacing data

    Bytes.decodeUtf8 returns Ok with the exact Unicode scalar sequence encoded by valid UTF-8 bytes.

    Empty Bytes returns Ok with an empty string. A leading UTF-8 encoding of U+FEFF remains in the returned string.

    Overlong encodings, isolated continuation bytes, incomplete sequences, surrogate encodings, and values above U+10FFFF return Err with a string.

    Decoding does not insert replacement characters, normalize scalars, alter newline sequences, or discard a byte order mark.

    For a string, encodeUtf8 followed by decodeUtf8 returns the original string. Re-encoding any successfully decoded sequence recovers its original bytes.

    The receiver evaluates once and remains unchanged. No text is exposed before the entire sequence has passed validation.

    Why

    Lossless decoding keeps malformed input visible and preserves distinctions needed by file formats, checksums, and text layout.

    Source evidence

    Retain a byte order mark and combining sequence

    Link
    main.tpzRetain a byte order mark and combining sequenceOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. for hex in (["", "efbbbf", "65cc81", "0d0a", "f09f9880"]) {
    5. let decoded = test.assertOk(test.assertOk(Bytes.fromHex(hex)).decodeUtf8())
    6. io.writeLine(decoded.encodeUtf8().toHex())
    7. }
    Expected outputexit 0
    
    efbbbf
    65cc81
    0d0a
    f09f9880
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. efbbbf
    2. 65cc81
    3. 0d0a
    4. f09f9880
    Source evidence

    Reject malformed UTF-8 categories

    Link
    main.tpzReject malformed UTF-8 categoriesOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. for hex in (["80", "c080", "e282", "eda080", "f4908080"]) {
    5. match (test.assertOk(Bytes.fromHex(hex)).decodeUtf8()) {
    6. case Err(_) => io.writeLine("invalid UTF-8")
    7. case Ok(_) => io.writeLine("accepted")
    8. }
    9. }
    Expected outputexit 0
    invalid UTF-8
    invalid UTF-8
    invalid UTF-8
    invalid UTF-8
    invalid UTF-8
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. invalid UTF-8
    2. invalid UTF-8
    3. invalid UTF-8
    4. invalid UTF-8
    5. invalid UTF-8
    Source evidence

    Allocate buffers and convert byte values

    Link
    Ruletopaz.stdlib.contract.byte-buffer-storage.rule

    Allocate buffers and convert byte values

    ByteBuffer.allocate accepts a nonnegative int length and an int initial value from 0 through 255. The initial value defaults to zero.

    The new buffer has exactly that many copies of the initial byte. Zero length is valid and still requires a valid initial byte.

    A negative length produces TPZ4001 at runtime. An invalid initial byte or an allocation that cannot be satisfied produces TPZ5001 at runtime.

    ByteBuffer.fromBytes converts an immutable Bytes value into a mutable ByteBuffer value containing the same bytes.

    ByteBuffer.toBytes converts the current buffer contents into an immutable Bytes value.

    ByteBuffer.length is a read-only byte count property. Buffer operations never change its length.

    Assignment, argument passing, return, capture, and aggregate storage follow collection value semantics.

    Supplied expressions evaluate once from left to right before allocation or conversion. These operations have no I/O effects.

    Why

    A fixed length gives binary algorithms stable byte positions. Explicit conversion distinguishes mutable construction from immutable byte data.

    Source evidence

    Allocate zero-filled and initialized buffers

    Link
    main.tpzAllocate zero-filled and initialized buffersOpen in Playground
    1. import std.bytes { ByteBuffer }
    2. import std.io
    3. io.writeLine(ByteBuffer.allocate(0).toBytes().toHex())
    4. io.writeLine(ByteBuffer.allocate(3).toBytes().toHex())
    5. io.writeLine(ByteBuffer.allocate(2, 255).toBytes().toHex())
    Expected outputexit 0
    
    000000
    ffff
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 000000
    2. ffff
    Source evidence

    Keep the original buffer and converted bytes unchanged

    Link
    main.tpzKeep the original buffer and converted bytes unchangedOpen in Playground
    1. import std.bytes { ByteBuffer }
    2. import std.io
    3. let source = "ab".encodeUtf8()
    4. let buffer = ByteBuffer.fromBytes(source)
    5. let independent = ByteBuffer.fromBytes(source)
    6. let before = buffer.toBytes()
    7. let mut alias = buffer
    8. alias.set(0, 99)
    9. io.writeLine(source.toHex())
    10. io.writeLine(before.toHex())
    11. io.writeLine(independent.toBytes().toHex())
    12. io.writeLine(buffer.toBytes().toHex())
    Expected outputexit 0
    6162
    6162
    6162
    6162
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 6162
    2. 6162
    3. 6162
    4. 6162
    Source evidence

    Reject a negative buffer length

    Link
    main.tpzReject a negative buffer lengthOpen in Playground
    1. import std.bytes { ByteBuffer }
    2. ByteBuffer.allocate(-1)
    Expected outputNonzero exit
    No stdout.
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during run. Nonzero exit.

    • TPZ4001 byte-buffer-negative-bound at main.tpz:2:1
    Source evidence

    Validate the initial byte of an empty buffer

    Link
    main.tpzValidate the initial byte of an empty bufferOpen in Playground
    1. import std.bytes { ByteBuffer }
    2. import std.io
    3. ByteBuffer.allocate(0, -1)
    4. io.writeLine("after")
    Expected outputNonzero exit
    No stdout.
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during run. Nonzero exit.

    • TPZ5001 byte-buffer-byte-value at main.tpz:3:1
    Source evidence

    Validate the initial byte of an empty buffer

    Link
    main.tpzValidate the initial byte of an empty bufferOpen in Playground
    1. import std.bytes { ByteBuffer }
    2. import std.io
    3. ByteBuffer.allocate(0, 256)
    4. io.writeLine("after")
    Expected outputNonzero exit
    No stdout.
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during run. Nonzero exit.

    • TPZ5001 byte-buffer-byte-value at main.tpz:3:1
    Source evidence

    Diagnostics

    Link

    Runtime value guard

    Validate buffer regions before writing

    Link
    Ruletopaz.stdlib.contract.byte-buffer-mutation.rule

    Validate buffer regions before writing

    ByteBuffer.get and ByteBuffer.set use zero-based byte indices from zero through length minus one. Get returns an int byte.

    ByteBuffer.set replaces one byte. ByteBuffer.fill replaces length bytes beginning at start. Both require values from 0 through 255.

    ByteBuffer.copy receives the source buffer, sourceStart, targetStart, and length in that order. The receiver is the destination.

    Fill and copy use byte intervals from each start to start plus length. Starts and lengths must be nonnegative. Every complete source and destination interval must fit its buffer without arithmetic overflow.

    A zero length region at the end of a buffer is valid. A start beyond the end is invalid even when length is zero.

    Copy observes the source interval before its first destination write. Copying within one buffer therefore supports overlap in either direction.

    Set, fill, and copy return unit. They require a let mut root binding and update its destination value under collection value semantics.

    The receiver and supplied arguments evaluate once from left to right. Every range and byte check finishes before the first write.

    An invalid index or region produces TPZ4001 at runtime. An invalid byte value produces TPZ5001 at runtime, including a fill of zero bytes.

    A rejected operation performs no buffer writes. Effects performed while evaluating its receiver or arguments remain visible.

    When several preconditions fail, the selected failing precondition is unspecified. The operation still completes every argument evaluation before validation and performs no writes.

    Why

    Checking complete regions prevents a malformed count from leaving half-written data. Reading the source interval before writing gives one result for every overlap direction.

    Source evidence

    Copy overlapping regions and preserve an earlier buffer value

    Link
    main.tpzCopy overlapping regions and preserve an earlier buffer valueOpen in Playground
    1. import std.bytes { Bytes, ByteBuffer }
    2. import std.io
    3. import std.test
    4. let mut buffer = ByteBuffer.fromBytes(test.assertOk(Bytes.fromHex("000102030405")))
    5. let alias = buffer
    6. buffer.copy(buffer, 0, 1, 5)
    7. io.writeLine(buffer.toBytes().toHex())
    8. buffer.fill(2, 2, 255)
    9. buffer.set(0, 128)
    10. io.writeLine(buffer.toBytes().toHex())
    11. buffer.copy(buffer, 1, 0, 5)
    12. buffer.fill(6, 0, 0)
    13. buffer.copy(buffer, 6, 6, 0)
    14. io.writeLine(buffer.toBytes().toHex())
    15. io.writeLine(alias.toBytes().toHex())
    16. io.writeLine("{alias.get(0)}")
    Expected outputexit 0
    000001020304
    8000ffff0304
    00ffff030404
    000102030405
    0
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 000001020304
    2. 8000ffff0304
    3. 00ffff030404
    4. 000102030405
    5. 0
    Source evidence

    Keep an independent source buffer unchanged

    Link
    main.tpzKeep an independent source buffer unchangedOpen in Playground
    1. import std.bytes { ByteBuffer }
    2. import std.io
    3. let source = ByteBuffer.fromBytes("abc".encodeUtf8())
    4. let mut target = ByteBuffer.allocate(4, 0)
    5. target.copy(source, 1, 1, 2)
    6. io.writeLine(target.toBytes().toHex())
    7. io.writeLine(source.toBytes().toHex())
    Expected outputexit 0
    00626300
    616263
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 00626300
    2. 616263
    Source evidence

    Reject a read at the exclusive end

    Link
    main.tpzReject a read at the exclusive endOpen in Playground
    1. import std.bytes { ByteBuffer }
    2. let buffer = ByteBuffer.allocate(2)
    3. buffer.get(2)
    Expected outputNonzero exit
    No stdout.
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during run. Nonzero exit.

    • TPZ4001 byte-buffer-index-bounds at main.tpz:3:1
    Source evidence

    Reject a negative write index

    Link
    main.tpzReject a negative write indexOpen in Playground
    1. import std.bytes { ByteBuffer }
    2. let mut buffer = ByteBuffer.allocate(2)
    3. buffer.set(-1, 0)
    Expected outputNonzero exit
    No stdout.
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during run. Nonzero exit.

    • TPZ4001 byte-buffer-negative-bound at main.tpz:3:1
    Source evidence

    Reject a fill whose complete region does not fit

    Link
    main.tpzReject a fill whose complete region does not fitOpen in Playground
    1. import std.bytes { ByteBuffer }
    2. let mut buffer = ByteBuffer.allocate(2)
    3. buffer.fill(1, 2, 0)
    Expected outputNonzero exit
    No stdout.
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during run. Nonzero exit.

    • TPZ4001 byte-buffer-region-bounds at main.tpz:3:1
    Source evidence

    Reject an empty copy beyond the buffer end

    Link
    main.tpzReject an empty copy beyond the buffer endOpen in Playground
    1. import std.bytes { ByteBuffer }
    2. let mut buffer = ByteBuffer.allocate(2)
    3. buffer.copy(buffer, 3, 2, 0)
    Expected outputNonzero exit
    No stdout.
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during run. Nonzero exit.

    • TPZ4001 byte-buffer-region-bounds at main.tpz:3:1
    Source evidence

    Reject an invalid byte at a valid write index

    Link
    main.tpzReject an invalid byte at a valid write indexOpen in Playground
    1. import std.bytes { ByteBuffer }
    2. import std.io
    3. let mut buffer = ByteBuffer.allocate(1)
    4. buffer.set(0, -1)
    5. io.writeLine("after")
    Expected outputNonzero exit
    No stdout.
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during run. Nonzero exit.

    • TPZ5001 byte-buffer-byte-value at main.tpz:4:1
    Source evidence

    Validate the byte for a fill of zero bytes

    Link
    main.tpzValidate the byte for a fill of zero bytesOpen in Playground
    1. import std.bytes { ByteBuffer }
    2. import std.io
    3. let mut buffer = ByteBuffer.allocate(1)
    4. buffer.fill(1, 0, 256)
    5. io.writeLine("after")
    Expected outputNonzero exit
    No stdout.
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Rejection during run. Nonzero exit.

    • TPZ5001 byte-buffer-byte-value at main.tpz:4:1
    Source evidence

    Compute interoperable checksums and digests

    Link
    Ruletopaz.stdlib.contract.bytes-digests.rule

    Compute interoperable checksums and digests

    Bytes.crc32 returns the unsigned CRC-32/ISO-HDLC value as an int from 0 through 4294967295.

    CRC-32 uses width 32, polynomial 0x04c11db7, reflected input and output, initial value 0xffffffff, and final XOR 0xffffffff.

    Bytes.sha256 and Bytes.sha512 implement FIPS 180-4 and return 32 and 64 digest bytes respectively.

    Bytes.hmacSha256 uses its receiver as the key and its argument as the message. It returns the 32-byte HMAC-SHA-256 value from RFC 2104.

    HMAC hashes a key longer than the 64-byte SHA-256 block before zero-padding it. Empty keys and messages are valid.

    Empty input is valid for every checksum and digest. CRC-32 of empty input is zero.

    These operations process stored bytes without decoding or normalization. Inputs remain unchanged and correctly typed calls have no data-dependent failure.

    The receiver and any message argument evaluate once in that order. No external state, random input, or I/O affects the result.

    Use toHex or toBase64 on a digest when a text encoding is required. Those encodings are separate from the digest bytes.

    Why

    Named standard algorithms must agree with independent implementations. Keeping digest bytes separate from their text representation makes the same digest usable in binary and text formats.

    Source evidence

    Match established checksum and hash vectors

    Link
    main.tpzMatch established checksum and hash vectorsOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. io.writeLine("{Bytes.empty().crc32()}")
    4. let checksum = "123456789".encodeUtf8().crc32()
    5. io.writeLine("{checksum}")
    6. io.writeLine("abc".encodeUtf8().sha256().toHex())
    7. io.writeLine("abc".encodeUtf8().sha512().toHex())
    8. io.writeLine("key".encodeUtf8().hmacSha256("The quick brown fox jumps over the lazy dog".encodeUtf8()).toHex())
    Expected outputexit 0
    0
    3421780262
    ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
    ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f
    f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 0
    2. 3421780262
    3. ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
    4. ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f
    5. f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8
    Source evidence

    Distinguish raw DEFLATE from zlib framing

    Link
    Ruletopaz.stdlib.contract.bytes-deflate.rule

    Distinguish raw DEFLATE from zlib framing

    Bytes.deflateCompress returns Ok with one complete raw RFC 1951 DEFLATE stream. It adds no zlib or gzip header or checksum.

    Bytes.deflateFixedCompress returns Ok with one final fixed Huffman block. Empty input is valid and the final unused bits are zero.

    Fixed Huffman compression accepts at most 268435456 input bytes. Larger inputs return Err with a string before producing output.

    Bytes.zlibFixedCompress applies the same fixed Huffman compression and input bound, with an RFC 1950 zlib header and Adler-32 trailer.

    The zlib stream declares a 32768-byte window and no preset dictionary. Its header bytes are 0x78 and 0x01.

    Bytes.deflateDecompress accepts raw streams containing stored, fixed Huffman, and dynamic Huffman blocks. It reconstructs the original bytes in Ok.

    The decoder consumes one complete stream. Malformed or truncated blocks, invalid back-references, and complete trailing bytes return Err with a string.

    Final unused padding bits are not decoded as data. A zlib or gzip wrapper is not a raw DEFLATE stream.

    Bytes.deflateDecompress reverses either raw compressor for every supported input. Bytes.zlibFixedCompress requires a zlib decoder because its wrapper is part of the result.

    Each receiver evaluates once and remains unchanged. The operations perform no I/O and errors expose no partially decoded Bytes.

    Compression does not promise a particular size reduction. Except for the fixed block and wrapper requirements, encoded byte choices may change while preserving the stream format.

    Why

    Raw DEFLATE and zlib are different wire formats. The fixed Huffman operation additionally specifies block coding, so it serves a distinct format requirement rather than another name for a general compressor.

    Source evidence

    Recover bytes from a raw stream

    Link
    main.tpzRecover bytes from a raw streamOpen in Playground
    1. import std.io
    2. import std.test
    3. let encoded = test.assertOk("hello".encodeUtf8().deflateCompress())
    4. io.writeLine(test.assertOk(encoded.deflateDecompress()).toHex())
    5. io.writeLine(test.assertOk("".encodeUtf8().deflateFixedCompress()).toHex())
    6. io.writeLine(test.assertOk("".encodeUtf8().zlibFixedCompress()).toHex())
    Expected outputexit 0
    68656c6c6f
    0300
    7801030000000001
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 68656c6c6f
    2. 0300
    3. 7801030000000001
    Source evidence

    Decode a valid fixed Huffman stream

    Link
    main.tpzDecode a valid fixed Huffman streamOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. let encoded = test.assertOk(Bytes.fromHex("cb48cdc9c90700"))
    5. match (encoded.deflateDecompress()) {
    6. case Ok(bytes) => io.writeLine(bytes.toHex())
    7. case Err(_) => io.writeLine("rejected")
    8. }
    Expected outputexit 0
    68656c6c6f
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 68656c6c6f
    Source evidence

    Decode short nonfinal stored blocks

    Link
    main.tpzDecode short nonfinal stored blocksOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. let encoded = test.assertOk(Bytes.fromHex("000100feff41010100feff42"))
    5. match (encoded.deflateDecompress()) {
    6. case Ok(bytes) => io.writeLine(bytes.toHex())
    7. case Err(_) => io.writeLine("rejected")
    8. }
    Expected outputexit 0
    4142
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 4142
    Source evidence

    Ignore stored block alignment bits

    Link
    main.tpzIgnore stored block alignment bitsOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. let encoded = test.assertOk(Bytes.fromHex("f90100feff41"))
    5. match (encoded.deflateDecompress()) {
    6. case Ok(bytes) => io.writeLine(bytes.toHex())
    7. case Err(_) => io.writeLine("rejected")
    8. }
    Expected outputexit 0
    41
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 41
    Source evidence

    Reject truncated streams and trailing bytes

    Link
    main.tpzReject truncated streams and trailing bytesOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. for hex in (["", "010100feff", "010000ffff00", "070000"]) {
    5. match (test.assertOk(Bytes.fromHex(hex)).deflateDecompress()) {
    6. case Err(_) => io.writeLine("invalid DEFLATE")
    7. case Ok(_) => io.writeLine("accepted")
    8. }
    9. }
    Expected outputexit 0
    invalid DEFLATE
    invalid DEFLATE
    invalid DEFLATE
    invalid DEFLATE
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. invalid DEFLATE
    2. invalid DEFLATE
    3. invalid DEFLATE
    4. invalid DEFLATE
    Source evidence

    Read and write complete gzip members

    Link
    Ruletopaz.stdlib.contract.bytes-gzip.rule

    Read and write complete gzip members

    Bytes.gzipCompress returns Ok with one RFC 1952 gzip member containing the receiver bytes. Empty input is valid.

    The compressor does not read a clock or derive a filename from its environment. The header timestamp is zero and optional name and comment fields are absent.

    Bytes.gzipDecompress accepts one member with compression method DEFLATE, including valid timestamp, operating system, extra field, name, comment, and header CRC fields.

    The decoder accepts stored, fixed Huffman, and dynamic Huffman DEFLATE blocks. It validates the member CRC-32 and uncompressed size modulo 4294967296.

    Reserved header flags, malformed optional fields, invalid compressed data, checksum mismatches, and truncation return Err with a string.

    A second member or other bytes after the member return Err. A Bytes value represents one complete compressed payload in this API.

    Decoding a successfully compressed value recovers the original bytes. A decoder failure never exposes a prefix of the output.

    The receiver evaluates once and remains unchanged. Compression and decompression perform no I/O and do not decode the payload as text.

    Why

    Standard header and block handling allows gzip data to move between programs. Requiring one complete member prevents unconsumed suffixes from being silently discarded.

    Source evidence

    Recover empty and nonempty payloads

    Link
    main.tpzRecover empty and nonempty payloadsOpen in Playground
    1. import std.io
    2. import std.test
    3. for text in (["", "hello"]) {
    4. let encoded = test.assertOk(text.encodeUtf8().gzipCompress())
    5. io.writeLine(test.assertOk(encoded.gzipDecompress()).toHex())
    6. }
    Expected outputexit 0
    
    68656c6c6f
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 68656c6c6f
    Source evidence

    Accept ordinary gzip header metadata

    Link
    main.tpzAccept ordinary gzip header metadataOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. let encoded = test.assertOk(Bytes.fromHex("1f8b0800010000000003010500faff68656c6c6f86a6103605000000"))
    5. match (encoded.gzipDecompress()) {
    6. case Ok(bytes) => io.writeLine(bytes.toHex())
    7. case Err(_) => io.writeLine("rejected")
    8. }
    Expected outputexit 0
    68656c6c6f
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 68656c6c6f
    Source evidence

    Accept compressed gzip blocks

    Link
    main.tpzAccept compressed gzip blocksOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. let encoded = test.assertOk(Bytes.fromHex("1f8b08000000000000ffcb48cdc9c9070086a6103605000000"))
    5. match (encoded.gzipDecompress()) {
    6. case Ok(bytes) => io.writeLine(bytes.toHex())
    7. case Err(_) => io.writeLine("rejected")
    8. }
    Expected outputexit 0
    68656c6c6f
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 68656c6c6f
    Source evidence

    Reject a truncated member and an invalid checksum

    Link
    main.tpzReject a truncated member and an invalid checksumOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. for hex in (["1f8b0800", "1f8b08000000000000ff010500faff68656c6c6f87a6103605000000"]) {
    5. match (test.assertOk(Bytes.fromHex(hex)).gzipDecompress()) {
    6. case Err(_) => io.writeLine("invalid gzip")
    7. case Ok(_) => io.writeLine("accepted")
    8. }
    9. }
    Expected outputexit 0
    invalid gzip
    invalid gzip
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. invalid gzip
    2. invalid gzip
    Source evidence

    Use Zstandard frames and explicit effort levels

    Link
    Ruletopaz.stdlib.contract.bytes-zstd.rule

    Use Zstandard frames and explicit effort levels

    Bytes.zstdCompress accepts an int level from 1 through 22. The default is 3 and other values return Err with a string.

    The level is a compression effort hint. The API does not promise a size ratio, execution time, or different bytes for every pair of levels.

    A successful call returns Ok with one RFC 8878 frame requiring no external dictionary. Empty input is valid.

    Bytes.zstdDecompress accepts one standard frame containing raw, RLE, or compressed blocks, with valid frame parameters and an optional content checksum.

    A frame requiring an unavailable dictionary, a malformed header or block, a size or checksum mismatch, and truncation return Err with a string.

    Skippable frames, additional frames, and other trailing bytes return Err. The entire input must represent one data frame.

    Decompression returns all uncompressed bytes in Ok or an error without a partial output. Compressing then decompressing preserves the input bytes.

    The receiver and explicit level expression evaluate once in that order. The input remains unchanged and the operation performs no I/O.

    Why

    A bounded level range keeps calls portable. A complete frame boundary and explicit dictionary failure make the returned bytes independent of hidden decoder state.

    Source evidence

    Use the default and both supported level boundaries

    Link
    main.tpzUse the default and both supported level boundariesOpen in Playground
    1. import std.io
    2. import std.test
    3. let source = "hello".encodeUtf8()
    4. io.writeLine(test.assertOk(test.assertOk(source.zstdCompress()).zstdDecompress()).toHex())
    5. for level in ([1, 22]) {
    6. io.writeLine(test.assertOk(test.assertOk(source.zstdCompress(level)).zstdDecompress()).toHex())
    7. }
    8. for level in ([0, 23]) {
    9. match (source.zstdCompress(level)) {
    10. case Err(_) => io.writeLine("invalid level")
    11. case Ok(_) => io.writeLine("accepted")
    12. }
    13. }
    Expected outputexit 0
    68656c6c6f
    68656c6c6f
    68656c6c6f
    invalid level
    invalid level
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 68656c6c6f
    2. 68656c6c6f
    3. 68656c6c6f
    4. invalid level
    5. invalid level
    Source evidence

    Decode a valid Zstandard RLE block

    Link
    main.tpzDecode a valid Zstandard RLE blockOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. let encoded = test.assertOk(Bytes.fromHex("28b52ffd20052b000041"))
    5. match (encoded.zstdDecompress()) {
    6. case Ok(bytes) => io.writeLine(bytes.toHex())
    7. case Err(_) => io.writeLine("rejected")
    8. }
    Expected outputexit 0
    4141414141
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 4141414141
    Source evidence

    Decode short nonfinal Zstandard raw blocks

    Link
    main.tpzDecode short nonfinal Zstandard raw blocksOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. let encoded = test.assertOk(Bytes.fromHex("28b52ffd20020800004109000042"))
    5. match (encoded.zstdDecompress()) {
    6. case Ok(bytes) => io.writeLine(bytes.toHex())
    7. case Err(_) => io.writeLine("rejected")
    8. }
    Expected outputexit 0
    4142
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 4142
    Source evidence

    Reject missing block data and trailing bytes

    Link
    main.tpzReject missing block data and trailing bytesOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. for hex in (["", "28b52ffd20052900006869", "28b52ffd200001000000"]) {
    5. match (test.assertOk(Bytes.fromHex(hex)).zstdDecompress()) {
    6. case Err(_) => io.writeLine("invalid Zstandard")
    7. case Ok(_) => io.writeLine("accepted")
    8. }
    9. }
    Expected outputexit 0
    invalid Zstandard
    invalid Zstandard
    invalid Zstandard
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. invalid Zstandard
    2. invalid Zstandard
    3. invalid Zstandard
    Source evidence

    Protect data with systematic Reed-Solomon codewords

    Link
    Ruletopaz.stdlib.contract.bytes-reed-solomon.rule

    Protect data with systematic Reed-Solomon codewords

    Bytes.reedSolomon255223Protect accepts between 1 and 14614305 input bytes. Empty input and larger input return Err with a string.

    Input bytes are partitioned into consecutive 223-byte data chunks. The final chunk is padded with zero bytes to 223 bytes.

    Each output codeword contains its 223 data bytes followed by 32 parity bytes. Codewords are concatenated in input order.

    The result contains 255 times the ceiling of input length divided by 223 bytes. At most 65535 codewords are produced.

    Field arithmetic uses GF256 with primitive polynomial 0x11d and primitive element 2. Generator roots are the powers of that element from 0 through 31.

    Treat the first data byte as the highest-degree coefficient. Parity is the 32-byte remainder after multiplying the data polynomial by x to the power 32.

    The output contains no original length or additional framing. Callers must retain the input length to distinguish padding from original trailing zero bytes.

    The operation generates parity bytes. It does not detect or correct corruption in existing codewords.

    The receiver evaluates once and remains unchanged. Input bounds are checked before output allocation and failure exposes no codeword prefix.

    Why

    A field definition and systematic layout make parity interoperable. Keeping the original length outside the codeword avoids silently guessing which trailing zero bytes belong to the payload.

    Source evidence

    Protect a zero byte with zero padding and parity

    Link
    main.tpzProtect a zero byte with zero padding and parityOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. import std.test
    4. let encoded = test.assertOk(test.assertOk(Bytes.fromArray([0])).reedSolomon255223Protect())
    5. io.writeLine("{encoded.toArray().length}")
    6. io.writeLine(encoded.slice(0, 4).toHex())
    7. io.writeLine(encoded.slice(223, 255).toHex())
    Expected outputexit 0
    255
    00000000
    0000000000000000000000000000000000000000000000000000000000000000
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 255
    2. 00000000
    3. 0000000000000000000000000000000000000000000000000000000000000000
    Source evidence

    Reject empty data before creating codewords

    Link
    main.tpzReject empty data before creating codewordsOpen in Playground
    1. import std.bytes { Bytes }
    2. import std.io
    3. match (Bytes.empty().reedSolomon255223Protect()) {
    4. case Err(_) => io.writeLine("empty data rejected")
    5. case Ok(_) => io.writeLine("accepted")
    6. }
    Expected outputexit 0
    empty data rejected
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. empty data rejected
    Source evidence

    Emit the specified parity for a nonzero payload

    Link
    main.tpzEmit the specified parity for a nonzero payloadOpen in Playground
    1. import std.io
    2. import std.test
    3. let encoded = test.assertOk("hello".encodeUtf8().reedSolomon255223Protect())
    4. io.writeLine(encoded.slice(0, 5).toHex())
    5. io.writeLine(encoded.slice(223, 255).toHex())
    Expected outputexit 0
    68656c6c6f
    c7a146d71a5245bf94210961b0acac5f4358ac66cf234be24bcea41117eeb5fd
    
    $ topaz run main.tpz · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 68656c6c6f
    2. c7a146d71a5245bf94210961b0acac5f4358ac66cf234be24bcea41117eeb5fd
    Source evidence

    Menu

    Download