-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
stapled.cr
131 lines (105 loc) · 2.92 KB
/
stapled.cr
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
# This class staples together two unidirectional `IO`s to form a single,
# bidirectional `IO`.
#
# Example (loopback):
# ```
# io = IO::Stapled.new(*IO.pipe)
# io.puts "linus"
# io.gets # => "linus"
# ```
#
# Most methods simply delegate to the underlying `IO`s.
class IO::Stapled < IO
# If `#sync_close?` is `true`, closing this `IO` will close the underlying `IO`s.
property? sync_close : Bool
# Returns `true` if this `IO` is closed.
#
# Underlying `IO`s might have a different status.
getter? closed : Bool = false
# Creates a new `IO::Stapled` which reads from *reader* and writes to *writer*.
def initialize(@reader : IO, @writer : IO, @sync_close : Bool = false)
end
# Reads a single byte from `reader`.
def read_byte : UInt8?
check_open
@reader.read_byte
end
# Reads a slice from `reader`.
def read(slice : Bytes)
check_open
@reader.read(slice)
end
# Gets a string from `reader`.
def gets(delimiter : Char, limit : Int, chomp = false) : String?
check_open
@reader.gets(delimiter, limit, chomp)
end
# Peeks into `reader`.
def peek : Bytes?
check_open
@reader.peek
end
# Skips `reader`.
def skip(bytes_count : Int) : Int64
check_open
@reader.skip(bytes_count)
end
# Skips `reader`.
def skip_to_end : Int64
check_open
@reader.skip_to_end
end
# Writes a byte to `writer`.
def write_byte(byte : UInt8) : Int64
check_open
@writer.write_byte(byte)
end
# Writes a slice to `writer`.
def write(slice : Bytes) : Int64
check_open
return 0i64 if slice.empty?
@writer.write(slice)
end
# Flushes `writer`.
def flush : self
check_open
@writer.flush
self
end
# Closes this `IO`.
#
# If `sync_close?` is `true`, it will also close the underlying `IO`s.
def close : Nil
return if @closed
@closed = true
if @sync_close
@reader.close
@writer.close
end
end
# Creates a pair of bidirectional pipe endpoints connected with each other
# and passes them to the given block.
#
# Both endpoints and the underlying `IO`s are closed after the block
# (even if `sync_close?` is `false`).
def self.pipe(read_blocking : Bool = false, write_blocking : Bool = false)
IO.pipe(read_blocking, write_blocking) do |a_read, a_write|
IO.pipe(read_blocking, write_blocking) do |b_read, b_write|
a, b = new(a_read, b_write, true), new(b_read, a_write, true)
begin
yield a, b
ensure
a.close
b.close
end
end
end
end
# Creates a pair of bidirectional pipe endpoints connected with each other
# and returns them in a `Tuple`.
def self.pipe(read_blocking : Bool = false, write_blocking : Bool = false) : {self, self}
a_read, a_write = IO.pipe(read_blocking, write_blocking)
b_read, b_write = IO.pipe(read_blocking, write_blocking)
return new(a_read, b_write, true), new(b_read, a_write, true)
end
end