Skip to content

Commit 0cec52c

Browse files
authored
implement stop-on-end stream (#25)
1 parent 7fce824 commit 0cec52c

File tree

10 files changed

+331
-135
lines changed

10 files changed

+331
-135
lines changed

docs/src/assets/modes.dot

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
11
digraph modes {
2+
"start" -> "idle";
3+
24
"idle" -> "read";
35
"idle" -> "write";
46
"idle" -> "close";
57
"idle" -> "panic";
68

79
"read" -> "read";
10+
"read" -> "stop";
811
"read" -> "close";
912
"read" -> "panic";
1013

1114
"write" -> "write";
15+
"write" -> "stop";
1216
"write" -> "close";
1317
"write" -> "panic";
18+
19+
"stop" -> "close";
20+
21+
"start" [ shape = point ];
22+
"idle" [ shape = circle ];
23+
"read" [ shape = circle ];
24+
"write" [ shape = circle ];
25+
"stop" [ shape = circle; style=bold; ];
26+
"close" [ shape = circle; style=bold; ];
27+
"panic" [ shape = circle; style=bold; ];
1428
}

docs/src/assets/modes.svg

Lines changed: 78 additions & 49 deletions
Loading

docs/src/devnotes.md

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,32 @@ default buffer size is 16KiB for each.
4444

4545
The `mode` field may be one of the following value:
4646
- `:idle` : initial and intermediate mode, no buffered data
47-
- `:read` : ready to read data, data may be buffered
48-
- `:write`: ready to write data, data may be buffered
47+
- `:read` : being ready to read data, data may be buffered
48+
- `:write`: being ready to write data, data may be buffered
49+
- `:stop` : transcoding is stopped, data may be buffered
4950
- `:close`: closed, no buffered data
5051
- `:panic`: an exception has been thrown in codec, data may be buffered but we
5152
cannot do anything
5253

54+
Note that `mode=:stop` does not mean there is no data available in the stream.
55+
This is because transcoded data may be left in the buffer.
56+
5357
The initial mode is `:idle` and mode transition happens as shown in the
5458
following diagram:
5559
![Mode transition](./assets/modes.svg)
5660

57-
The mode transition should happen in the `changemode!(stream, newmode)` function
58-
in src/stream.jl. Trying an undefined transition will thrown an exception.
59-
60-
A transition happens based on actions (or function calls) of the user or return
61-
code of the codec. For example, calling `read(stream)` will change the mode from
62-
`:init` to `:read` and then calling `close(stream)` will change the mode from
63-
`:read` to `:close`. When data processing fails in the codec, a codec will
64-
return `:error` and the stream will result in `:panic`.
61+
Modes surrounded by a bold circle are a state in which the transcoding stream
62+
has released resources by calling `finalize(codec)`. The mode transition should
63+
happen in the `changemode!(stream, newmode)` function in src/stream.jl. Trying
64+
an undefined transition will thrown an exception.
65+
66+
A transition happens according to internal or external events of the transcoding
67+
stream. The status code and the error object returned by codec methods are
68+
internal events, and user's method calls are external events. For example,
69+
calling `read(stream)` will change the mode from `:init` to `:read` and then
70+
calling `close(stream)` will change the mode from `:read` to `:close`. When data
71+
processing fails in the codec, a codec will return `:error` and the stream will
72+
result in `:panic`.
6573

6674

6775
Shared buffers

docs/src/examples.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,34 @@ Effectively, this is equivalent to the following pipeline:
132132

133133
cat data.txt.gz | gzip -d | zstd >data.txt.zst
134134

135+
Stop decoding on the end of a block
136+
-----------------------------------
137+
138+
Most codecs support decoding concatenated data blocks. For example, if you
139+
concatenate two gzip files into a file and read it using
140+
`GzipDecompressionStream`, you will see the byte stream of concatenation of two
141+
files. If you need the first part of the file, you can set `stop_on_end` to
142+
`true` to stop transcoding at the end of the first block:
143+
```julia
144+
using CodecZlib
145+
# cat foo.txt.gz bar.txt.gz > foobar.txt.gz
146+
stream = GzipDecompressionStream(open("foobar.txt.gz"), stop_on_end=true)
147+
read(stream) #> the content of foo.txt
148+
eof(stream) #> true
149+
```
150+
151+
In the case where you need to reuse the wrapped stream, the code above must be
152+
slightly modified because the transcoding stream may read more bytes than
153+
necessary from the wrapped stream. By wrapping a stream with `NoopStream`, the
154+
problem of overreading is resolved:
155+
```julia
156+
using CodecZlib
157+
using TranscodingStreams
158+
stream = NoopStream(open("foobar.txt.gz"))
159+
read(GzipDecompressionStream(stream, stop_on_end=true)) #> the content of foo.txt
160+
read(GzipDecompressionStream(stream, stop_on_end=true)) #> the content of bar.txt
161+
```
162+
135163
Transcode data in one shot
136164
--------------------------
137165

src/buffer.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ function reset!(buf::Buffer)
139139
return buf.bufferpos
140140
end
141141

142-
# Make margin with ≥`minsize`.
142+
# Make margin with ≥`minsize` and return the size of it.
143143
function makemargin!(buf::Buffer, minsize::Integer)
144144
@assert minsize 0
145145
if buffersize(buf) == 0 && buf.markpos == 0

src/noop.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ function flushbufferall(stream::NoopStream)
155155
return bufsize
156156
end
157157

158-
function processall(stream::NoopStream)
159-
flushbufferall(stream)
160-
@assert buffersize(stream.state.buffer1) == 0
158+
function flushuntilend(stream::NoopStream)
159+
writedata!(stream.stream, stream.state.buffer1)
160+
return
161161
end

src/state.jl

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
# See docs/src/devnotes.md.
55
mutable struct State
66
# current stream mode
7-
mode::Symbol
7+
mode::Symbol # {:idle, :read, :write, :stop, :close, :panic}
88

9-
# return code of the last method call (:ok or :end)
10-
code::Symbol
9+
# return code of the last method call
10+
code::Symbol # {:ok, :end, :error}
11+
12+
# flag to go :stop on :end
13+
stop_on_end::Bool
1114

1215
# exception thrown while data processing
1316
error::Error
@@ -16,11 +19,11 @@ mutable struct State
1619
buffer1::Buffer
1720
buffer2::Buffer
1821

19-
function State(size::Integer)
20-
return new(:idle, :ok, Error(), Buffer(size), Buffer(size))
21-
end
22-
2322
function State(buffer1::Buffer, buffer2::Buffer)
24-
return new(:idle, :ok, Error(), buffer1, buffer2)
23+
return new(:idle, :ok, false, Error(), buffer1, buffer2)
2524
end
2625
end
26+
27+
function State(size::Integer)
28+
return State(Buffer(size), Buffer(size))
29+
end

0 commit comments

Comments
 (0)