You are looking at historical revision 40971 of this page. It may differ significantly from its current revision.

SRFI-207: String-notated bytevectors

To ease the human reading and writing of Scheme code involving binary data that for mnemonic reasons corresponds as a whole or in part to ASCII-coded text, a notation for bytevectors is defined which allows printable ASCII characters to be used literally without being converted to their corresponding integer forms. In addition, this SRFI provides a set of procedures known as the bytestring library for constructing a bytevector from a sequence of integers, characters, strings, and/or bytevectors, and for manipulating bytevectors as if they were strings as far as possible.

This egg provides both the bytestring library and a reader hook supporting the notation. To use bytestring literals in compiled code, compile with -X srfi-207.

SRFI Description

This page includes excerpts from the SRFI document, but is primarily intended to document the forms exported by the egg. For a full description of the SRFI, see the SRFI document.

Specification

Most of the procedures of this SRFI begin with bytestring- in order to distinguish them from other bytevector procedures. This does not mean that they accept or return a separate bytestring type: bytestrings and bytevectors are exactly the same type.

The following names are used for the arguments:

obj Any Scheme object.
bytevector A bytevector.
pred A predicate that accepts zero or more arguments.
list A Scheme list.
port A port.
string A string.
start, end Exact integers specifying a half-open interval of indexes for a sub-bytevector. When omitted, start defaults to 0 and end to the length of the corresponding bytevector argument. It is an error unless 0 ≤ startend(bytevector-length bytevector).

It is an error (unless otherwise noted) if the procedures are passed arguments that do not have the type implied by the argument names.

External notation

The basic form of a string-notated bytevector is:

   #u8" content "

To avoid character encoding issues within string-notated bytevectors, only printable ASCII characters (that is, Unicode codepoints in the range from U+0020 to U+007E inclusive) are allowed to be used within the content of a string-notated bytevector. All other characters must be expressed through mnemonic or inline hex escapes, and " and \ must also be escaped as in normal Scheme strings.

Within the content of a string-notated bytevector:

Seq. Integer
\a 7
\b 8
\t 9
\n 10
\r 13
\| 124

Note: The \| sequence is provided so that string parsing, symbol parsing, and string-notated bytevector parsing can all use the same sequences. However, we give a complete definition of the valid lexical syntax in this SRFI rather than inheriting the native syntax of strings, so that it is clear that #u8"ι" and #u8"\xE000;" are invalid.

When the Scheme reader encounters a string-notated bytevector, it produces a datum as if that bytevector had been written out in full. That is, #u8"A" is exactly equivalent to #u8(65).

Formal syntax

The formal syntax of Scheme (defined in R7RS-small 7.1) is amended as follows.

⟨string-notated bytevector⟩ → #u8" ⟨string-notated bytevector element⟩* "
⟨string-notated bytevector element⟩ → ⟨any printable ASCII character other than " or \⟩
  | ⟨mnemonic escape⟩ | \" | \\
  | \⟨intraline whitespace⟩*⟨line ending⟩⟨intraline whitespace⟩*
  | ⟨inline hex escape⟩

Constructors

[procedure] (bytestring arg …)

Converts the args into a sequence of small integers and returns them as a bytevector as follows:

Otherwise, an error satisfying bytestring-error? is signaled.

Examples:

(bytestring "lor" #\r #x65 #u8(#x6d)) ⇒ #u8"lorem"
(bytestring "η" #\space #u8(#x65 #x71 #x75 #x69 #x76)); error
[procedure] (make-bytestring list)

If the elements of list are suitable arguments for bytestring, returns the bytevector that would be the result of applying bytestring to list. Otherwise, an error satisfying bytestring-error? is signaled.

[procedure] (make-bytestring! bytevector at list)

If the elements of list are suitable arguments for bytestring, writes the bytes of the bytevector that would be the result of calling make-bytestring into bytevector starting at index at.

(define bstring (make-bytevector 10 #x20))
(make-bytestring! bstring 2 '(#\s #\c "he" #u8(#x6d #x65))
bstring ⇒ #u8"  scheme  "

Conversion

[procedure] (bytevector->hex-string bytevector)
[procedure] (hex-string->bytevector string)

Converts between a bytevector and a string containing pairs of hexadecimal digits. If string is not pairs of hexadecimal digits, an error satisfying bytestring-error? is raised.

(bytevector->hex-string #u8"Ford")"467f7264"
(hex-string->bytevector "5a6170686f64") ⇒ #u8"Zaphod"
[procedure] (bytevector->base64 bytevector [digits])
[procedure] (base64->bytevector string [digits])

Converts between a bytevector and its base-64 encoding as a string. The 64 digits are represented by the characters 0–9, A–Z, a–z, and the symbols + and /. However, there are different variants of base-64 encoding which use different representations of the 62nd and 63rd digit. If the optional argument digits (a two-character string) is provided, those two characters will be used as the 62nd and 63rd digit instead. Details can be found in RFC 4648. If string is not in base-64 format, an error satisfying bytestring-error? is raised. However, characters that satisfy char-whitespace? are silently ignored.

(bytevector->base64 #u8(1 2 3 4 5 6))"AQIDBAUG"
(bytevector->base64 #u8"Arthur Dent")"QXJ0aHVyIERlbnQ="
(base64->bytevector "+/     /+") ⇒ #u8(#xfb #xff #xfe)
[procedure] (bytestring->list bytevector [ start [ end ] ])

Converts all or part of bytevector into a list of the same length containing characters for elements in the range 32 to 127 and exact integers for all other elements.

(bytestring->list #u8(#x41 #x42 1 2) 1 3)(#\B 1)
[procedure] (make-bytestring-generator arg …)

Returns a SRFI 158 generator that when invoked will return consecutive bytes of the bytevector that bytestring would create when applied to args, but without creating any bytevectors. The args are validated before any bytes are generated; if they are ill-formed, an error satisfying bytestring-error? is raised.

(generator->list (make-bytestring-generator "lorem"))(#x6c #x6f #x72 #x65 #x6d)

Selection

[procedure] (bytestring-pad bytevector len char-or-u8)
[procedure] (bytestring-pad-right bytevector len char-or-u8)

Returns a newly allocated bytevector with the contents of bytevector plus sufficient additional bytes at the beginning/end containing char-or-u8 (which can be either an ASCII character or an exact integer in the range 0–255) such that the length of the result is at least len.

(bytestring-pad #u8"Zaphod" 10 #\_) ⇒ #u8"____Zaphod"
(bytestring-pad-right #u8(#x80 #x7f) 8 0) ⇒ #u8(#x80 #x7f 0 0 0 0 0 0)
[procedure] (bytestring-trim bytevector pred)
[procedure] (bytestring-trim-right bytevector pred)
[procedure] (bytestring-trim-both bytevector pred)

Returns a newly allocated bytevector with the contents of bytevector, except that consecutive bytes at the beginning / the end / both the beginning and the end that satisfy pred are not included.

(bytestring-trim #u8"   Trillian" (lambda (b) (= b #x20)))
  ⇒ #u8"Trillian"
(bytestring-trim-both #u8(0 0 #x80 #x7f 0 0 0) zero?) ⇒ #u8(#x80 #x7f)

Replacement

[procedure] (bytestring-replace bytevector₁ bytevector₂ start₁ end₁ [start₂ end₂])

Returns a newly allocated bytevector with the contents of bytevector₁, except that the bytes indexed by start₁ and end₁ are not included but are replaced by the bytes of bytevector₂ indexed by start₂ and end₂.

(bytestring-replace #u8"Vogon torture" #u8"poetry" 6 13)
  ⇒ #u8"Vogon poetry"

Comparison

To compare bytevectors for equality, use the procedure bytevector=? from either the R6RS library (rnrs bytevectors) or the equivalent R7RS library (scheme bytevector). (Note: These libraries are not yet present in CHICKEN.)

[procedure] (bytestring<? bytevector₁ bytevector₂)
[procedure] (bytestring>? bytevector₁ bytevector₂)
[procedure] (bytestring<=? bytevector₁ bytevector₂)
[procedure] (bytestring>=? bytevector₁ bytevector₂)

Returns #t if bytevector₁ is less than / greater than / less than or equal to / greater than or equal to bytevector₂. Comparisons are lexicographical: shorter bytevectors compare before longer ones, all elements being equal.

(bytestring<? #u8"Heart Of Gold" #u8"Heart of Gold") ⇒ #t
(bytestring<=? #u8(#x81 #x95) #u8(#x80 #xa0)) ⇒ #f
(bytestring>? #u8(1 2 3) #u8(1 2)) ⇒ #t

Searching

[procedure] (bytestring-index bytevector pred [start [end]])
[procedure] (bytestring-index-right bytevector pred [start [end]])

Searches bytevector from start to end / from end to start for the first byte that satisfies pred, and returns the index into bytevector containing that byte. In either direction, start is inclusive and end is exclusive. If there are no such bytes, returns #f.

(bytestring-index #u8(#x65 #x72 #x83 #x6f) (lambda (b) (> b #x7f))) ⇒ 2
(bytestring-index #u8"Beeblebrox" (lambda (b) (> b #x7f))) ⇒ #f
(bytestring-index-right #u8"Zaphod" odd?) ⇒ 4
[procedure] (bytestring-break bytevector pred)
[procedure] (bytestring-span bytevector pred)

Returns two values, a bytevector containing the maximal sequence of characters (searching from the beginning of bytevector to the end) that do not satisfy / do satisfy pred, and another bytevector containing the remaining characters.

(bytestring-break #u8(#x50 #x4b 0 0 #x1 #x5) zero?)
  ⇒ #u8(#x50 #x4b)
    #u8(0 0 #x1 #x5)
(bytestring-span #u8"ABCDefg" (lambda (b) (and (> b 40) (< b 91))))
  ⇒ #u8"ABCD"
    #u8"efg"

Joining and splitting

[procedure] (bytestring-join bytevector-list delimiter [grammar])

Pastes the bytevectors in bytevector-list together using the delimiter, which can be anything suitable as an argument to bytestring. The grammar argument is a symbol that determines how the delimiter is used, and defaults to infix. It is an error for grammar to be any symbol other than these four:

(bytestring-join '(#u8"Heart" #u8"of" #u8"Gold") #x20) ⇒ #u8"Heart of Gold"
(bytestring-join '(#u8(#xef #xbb) #u8(#xbf)) 0 'prefix) ⇒ #u8(0 #xef #xbb 0 #xbf)
(bytestring-join '() 0 'strict-infix); error
[procedure] (bytestring-split bytevector delimiter [grammar])

Divides the elements of bytevector and returns a list of newly allocated bytevectors using the delimiter (an ASCII character or exact integer in the range 0–255 inclusive). Delimiter bytes are not included in the result bytevectors.

The grammar argument is used to control how bytevector is divided. It has the same default and meaning as in bytestring-join, except that infix and strict-infix mean the same thing. That is, if grammar is prefix or suffix, then ignore any delimiter in the first or last position of bytevector respectively.

(bytestring-split #u8"Beeblebrox" #x62)(#u8"Bee" #u8"le" #u8"rox")
(bytestring-split #u8(1 0 2 0) 0 'suffix)(#u8(1) #u8(2))

I/O

[procedure] (read-textual-bytestring prefix [ port ])

Reads a string in the external format described in this SRFI from port and return it as a bytevector. If the prefix argument is false, this procedure assumes that "#u8" has already been read from port. If port is omitted, it defaults to the value of (current-input-port). If the characters read are not in the external format, an error satisfying bytestring-error? is raised.

(call-with-port (open-input-string "#u8\"AB\\xad;\\xf0;\\x0d;CD\"")
                (lambda (port)
                  (read-textual-bytestring #t port)))
  ⇒ #u8(#x41 #x42 #xad #xf0 #x0d #x43 #x44)
[procedure] (write-textual-bytestring bytevector [ port ])

Writes bytevector in the external format described in this SRFI to port. Bytes representing non-graphical ASCII characters are unencoded: all other bytes are encoded with a single letter if possible, otherwise with a \x escape. If port is omitted, it defaults to the value of (current-output-port).

(call-with-port (open-output-string)
                (lambda (port)
                  (write-textual-bytestring
                   #u8(#x9 #x41 #x72 #x74 #x68 #x75 #x72 #xa)
                   port)
                  (get-output-string port)))"#u8\"\\tArthur\\n\""
[procedure] (write-binary-bytestring port arg …)

Outputs each arg to the binary output port port using the same interpretations as bytestring, but without creating any bytevectors. The args are validated before any bytes are written to port; if they are ill-formed, an error satisfying bytestring-error? is raised.

(call-with-port (open-output-bytevector)
                (lambda (port)
                  (write-binary-bytestring port #\Z #x61 #x70 "hod")
                  (get-output-bytevector port)))
  ⇒ #u8"Zaphod"

Exception

[procedure] (bytestring-error? obj)

Returns #t if obj is an object signaled by any of the following procedures, in the circumstances described above:

Like R7RS error objects, the bytestring-error objects provided by this implementation encapsulate a message and a collection of irritants. The former is a string; the latter can be any Scheme objects, generally those which caused the error to be signaled. As an extension, the following additional procedures for inspecting bytestring-error objects are also provided.

[procedure] (bytestring-error-message error-obj)

(Extension) Returns the message (a string) encapsulated by error-obj, which must be an object satisfying bytestring-error?.

[procedure] (bytestring-error-irritants error-obj)

(Extension) Returns a list of the irritants encapsulated by error-obj, which must be an object satisfying bytestring-error?.

About This Egg

Dependencies

The following eggs are required:

In addition, to run the provided tests, the srfi-4, srfi-158, and test eggs are needed.

Authors

by Daphne Preston-Kendal (external notation), John Cowan (procedure design), & Wolfgang Corcoran-Mathe (implementation)

Ported to Chicken Scheme 5 by Sergey Goldgaber.

Maintainer

Wolfgang Corcoran-Mathe

Contact: <wcm at sigwinch dot xyzzy minus the zy>

Repository

GitHub

© 2020 Daphne Preston-Kendal, John Cowan, and Wolfgang Corcoran-Mathe.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Version history

0.1
Ported to Chicken Scheme 5
0.2
Changed maintainer information.
0.2.1
Simplified dependencies.
0.3
Reader support for bytestring literals, types.