File: README.md

package info (click to toggle)
acl2 8.6%2Bdfsg-3
  • links: PTS
  • area: main
  • in suites: sid
  • size: 1,138,276 kB
  • sloc: lisp: 17,818,294; java: 125,359; python: 28,122; javascript: 23,458; cpp: 18,851; ansic: 11,569; perl: 7,678; xml: 5,591; sh: 3,978; makefile: 3,840; ruby: 2,633; yacc: 1,126; ml: 763; awk: 295; csh: 233; lex: 197; php: 178; tcl: 49; asm: 23; haskell: 17
file content (142 lines) | stat: -rw-r--r-- 6,509 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# fast-io

**Now with
[static-vectors](https://github.com/sionescu/static-vectors)
support!**

```lisp
(deftype octet '(unsigned-byte 8))
(deftype octet-vector '(simple-array octet (*)))
```

Fast-io is about improving performance to octet-vectors and octet
streams (though primarily the former, while wrapping the latter).
Imagine we're creating messages for the network. If we try and fill an
octet-vector with 50 bytes, 50000 times, here are the results (SBCL
1.0.57):

<table>
<tr>
  <th></th>
  <th align=right><tt>vector-push-extend</tt>:</th>
  <th align=right><tt>flexi-streams</tt>:</th>
  <th align=right><tt>fast-io</tt>:</th>
</tr>
<tr>
  <td align=right>Time:</td>
  <td align=right>0.767s</td>
  <td align=right>2.545s</td>
  <td align=right>0.090s</td>
</tr>
<tr>
  <td align=right>Bytes consed:</td>
  <td align=right>104,778,352</td>
  <td align=right>274,452,768</td>  
  <td align=right>18,373,904</td>
</tr>
</table>

(See `t/benchmarks.lisp` for the exact code used.)

It *should* be surprising that it takes a nontrivial effort to achieve
relatively decent performance to octet-vectors, but probably isn't.
However, fast-io provides a relatively straightforward interface for
reading and writing either a stream or a vector:

```lisp
;;; Write a byte or sequence, optionally to a stream:

(with-fast-output (buffer [STREAM | :vector | :static])
  (fast-write-byte BYTE buffer))

(with-fast-output (buffer [STREAM | :vector | :static])
  (fast-write-sequence OCTET-VECTOR buffer [START [END]]))

;;; Read from a vector or stream:

(with-fast-input (buffer VECTOR [STREAM])
  (fast-read-byte buffer))

(with-fast-input (buffer VECTOR [STREAM])
  (let ((vec (make-octet-vector N)))
    (fast-read-sequence vec buffer [START [END]])))
```

## Multi-byte and Endianness

Fast-io provides a host of read and write functions for big- and little-endian reads.  See the [Dictionary](#reading-and-writing) below.

## Static Vectors

You may now specify `:static` instead of a stream to
`WITH-OUTPUT-BUFFER`.  This returns an octet-vector created with
[static-vectors](https://github.com/sionescu/static-vectors),
which means that passing the buffered data directly to a foreign
function is now that much more efficient:

```lisp
(let ((data (with-fast-output (buffer :static)
              (buffer-some-data buffer))))
  (foreign-send (static-vectors:static-vector-pointer data))
  (static-vectors:free-static-vector data))
```

Note that the restriction for manually freeing the result remains.
This avoids multiple inefficient (i.e., byte-by-byte) copies to
foreign memory.

## Streams

Obviously, the above API isn't built around Lisp streams, or even
gray-streams.  However, fast-io provides a small wrapper using
`trivial-gray-streams`, and supports `{WRITE,READ}-SEQUENCE`:

```lisp
(let ((stream (make-instance 'fast-io:fast-output-stream)))
  (write-sequence (fast-io:octets-from '(1 2 3 4)) stream))
```

Both `fast-input-stream` and `fast-output-stream` support backing a
stream, much like using the plain fast-io buffers.  However, using the
gray-streams interface is a 3-4x as slow as using the buffers alone.
Simple benchmarks show the gray-streams interface writing 1M 50-byte
vectors in about 1.7s, whereas simply using buffers is about 0.8s.
Consing remains similar between the two.

## Dictionary

### Octets

Most functions operate on or require octet-vectors, i.e.,

```lisp
(deftype octet () '(unsigned-byte 8))
(deftype octet-vector '(simple-array octet (*)))
```

Which is exactly what is defined and exported from `fast-io`.  Also:

* `make-octet-vector LEN`<br> Make an octet-vector of length `LEN`.
* `octets-from SEQUENCE`<br> Make an octet-vector from the contents of `SEQUENCE`.

### Buffers

* `make-input-buffer &key VECTOR STREAM POS`<br> Create an input buffer for use with input functions.  `:vector` specifies the vector to be read from.  `:stream` specifies the stream to read from.  `:pos` specifies the offset to start reading into `VECTOR` If both `:vector` and `:stream` is provided, the input buffer reads from the vector first, followed by the stream.
* `make-output-buffer &key OUTPUT`<br> Create an output buffer for use with output functions. `:output` specifies an output stream.  If `:output :static` is specified, and static-vectors is supported, output will be to a static-vector.
* `finish-output-buffer BUFFER`<br> Finish the output and return the complete octet-vector.
* `buffer-position BUFFER`<br> Return the current read/write position for `BUFFER`.

* `with-fast-input (BUFFER VECTOR &optional STREAM (OFFSET 0)) &body body`<br> Create an input buffer called `BUFFER`, optionally reading from `VECTOR`, followed by reading from `STREAM`.  If `OFFSET` is specified, start reading from this position in `VECTOR`.
* `with-fast-output (BUFFER &optional OUTPUT) &body BODY`<br> Create an output buffer named `BUFFER`, optionally writing to the stream `OUTPUT`.  This will automatically `FINISH-OUTPUT-BUFFER` on `BUFFER`.  Thus the `with-fast-output` form evaluates to the completed octet-vector.

### Reading and Writing

* `fast-read-byte INPUT-BUFFER &optional (EOF-ERROR-P t) EOF-VALUE`<br> Read a byte from `INPUT-BUFFER`.  If `EOF-ERROR-P` is `t`, reading past the end-of-file will signal `CL:END-OF-FILE`.  Otherwise, it will return `EOF-VALUE` instead.
* `fast-write-byte BYTE OUTPUT-BUFFER`<br> Write a byte to `OUTPUT-BUFFER`.
* `fast-read-sequence SEQUENCE INPUT-BUFFER &optional (START 0) END`<br> Read from `INPUT-BUFFER` into `SEQUENCE`.  Values will be written starting at position `START` and, if `END` is specified, ending at `END`.  Otherwise values will be written until the length of the sequence, or until the input is exhausted.
* `fast-write-sequence SEQUENCE OUTPUT-BUFFER &optional (START 0) END`<br> Write `SEQUENCE` to `OUTPUT-BUFFER`, starting at position `START` in `SEQUENCE`.  If `END` is specified, values will be written until `END`; otherwise, values will be written for the length of the sequence.

For multi-byte reads and writes requiring endianness, fast-io provides functions in the following forms:

* `write[u]{8,16,32,64,128}{-be,-le}`: E.g., `(write32-be VALUE BUFFER)` will write the specified 32-bit value to the specified buffer with a *big-endian* layout.  Likewise, `(writeu16-le VALUE BUFFER)` will write an *unsigned* 16-bit value in *little-endian* layout.
* `read[u]{8,16,32,64,128}{-be,-le}`: Similarly, `(read64-le BUFFER)` will read a 64-bit value from the buffer with little-endian layout.