-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
sized.cr
79 lines (66 loc) · 2.02 KB
/
sized.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
module IO
# An IO that wraps another IO, setting a limit for the number of bytes that can be read.
#
# ```
# io = IO::Memory.new "abcde"
# sized = IO::Sized.new(io, read_size: 3)
#
# sized.gets_to_end # => "abc"
# sized.gets_to_end # => ""
# io.gets_to_end # => "de"
# ```
class Sized
include IO
# If `#sync_close?` is `true`, closing this IO will close the underlying IO.
property? sync_close : Bool
# The number of remaining bytes to be read.
getter read_remaining : UInt64
getter? closed : Bool
# Creates a new `IO::Sized` which wraps *io*, and can read a maximum of
# *read_size* bytes. If *sync_close* is set, calling `#close` calls
# `#close` on the underlying IO.
def initialize(@io : IO, read_size : Int, @sync_close = false)
raise ArgumentError.new "negative read_size" if read_size < 0
@closed = false
@read_remaining = read_size.to_u64
end
def read(slice : Bytes)
check_open
count = {slice.size.to_u64, @read_remaining}.min
bytes_read = @io.read slice[0, count]
@read_remaining -= bytes_read
bytes_read
end
def read_byte
check_open
if @read_remaining > 0
byte = @io.read_byte
@read_remaining -= 1 if byte
byte
else
nil
end
end
def gets(delimiter : Char, limit : Int, chomp = false) : String?
check_open
return super if @encoding
return nil if @read_remaining == 0
# We can't pass chomp here, because it will remove part of the delimiter
# and then we won't know how much we consumed from @io, so we chomp later
string = @io.gets(delimiter, Math.min(limit, @read_remaining))
if string
@read_remaining -= string.bytesize
string = string.chomp(delimiter) if chomp
end
string
end
def write(slice : Bytes)
raise IO::Error.new "Can't write to IO::Sized"
end
def close
return if @closed
@closed = true
@io.close if @sync_close
end
end
end