-
-
Notifications
You must be signed in to change notification settings - Fork 154
Expand file tree
/
Copy pathreader.go
More file actions
241 lines (209 loc) · 6.49 KB
/
reader.go
File metadata and controls
241 lines (209 loc) · 6.49 KB
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
package dicomio
import (
"bufio"
"compress/flate"
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"github.com/suyashkumar/dicom/pkg/charset"
"golang.org/x/text/encoding"
)
var (
// ErrorInsufficientBytesLeft indicates there are not enough bytes left in
// the current buffer (or enough bytes left until the currently set limit)
// to complete the operation.
ErrorInsufficientBytesLeft = errors.New("not enough bytes left until buffer limit to complete this operation")
// ErrorLimitStackEmpty indicates that PopLimit was called on a reader with no
// limits on the stack.
ErrorLimitStackEmpty = errors.New("pop limit called on empty stack")
)
// LimitReadUntilEOF is a special dicomio.Reader limit indicating that there is no hard limit and the
// Reader should read until EOF.
const LimitReadUntilEOF = -9999
type Reader struct {
in *bufio.Reader
bo binary.ByteOrder
implicit bool
limit int64
bytesRead int64
limitStack []int64
// cs represents the CodingSystem to use when reading the string. If a
// particular encoding.Decoder within this CodingSystem is nil, assume
// UTF-8.
cs charset.CodingSystem
}
// NewReader creates and returns a new *dicomio.Reader.
func NewReader(in *bufio.Reader, bo binary.ByteOrder, limit int64) *Reader {
return &Reader{
in: in,
bo: bo,
limit: limit,
bytesRead: 0,
}
}
func (r *Reader) BytesLeftUntilLimit() int64 {
if r.limit == LimitReadUntilEOF {
return math.MaxInt64
}
return r.limit - r.bytesRead
}
func (r *Reader) Read(p []byte) (int, error) {
// Check if we've hit the limit
if r.BytesLeftUntilLimit() <= 0 {
if len(p) == 0 {
return 0, nil
}
return 0, io.EOF
}
// If asking for more than we have left, just return whatever we've got left
// TODO: return a special kind of error if this situation occurs to inform the caller
if int64(len(p)) > r.BytesLeftUntilLimit() {
p = p[:r.BytesLeftUntilLimit()]
}
n, err := r.in.Read(p)
if n >= 0 {
r.bytesRead += int64(n)
}
return n, err
}
// ReadUInt8 reads an uint8 from the underlying *Reader.
func (r *Reader) ReadUInt8() (uint8, error) {
var out uint8
err := binary.Read(r, r.bo, &out)
return out, err
}
// ReadUInt16 reads an uint16 from the underlying *Reader.
func (r *Reader) ReadUInt16() (uint16, error) {
var out uint16
err := binary.Read(r, r.bo, &out)
return out, err
}
// ReadUInt32 reads an uint32 from the underlying *Reader.
func (r *Reader) ReadUInt32() (uint32, error) {
var out uint32
err := binary.Read(r, r.bo, &out)
return out, err
}
// ReadInt16 reads an int16 from the underlying *Reader.
func (r *Reader) ReadInt16() (int16, error) {
var out int16
err := binary.Read(r, r.bo, &out)
return out, err
}
// ReadInt32 reads an int32 from the underlying *Reader.
func (r *Reader) ReadInt32() (int32, error) {
var out int32
err := binary.Read(r, r.bo, &out)
return out, err
}
// ReadFloat32 reads a float32 from the underlying *Reader.
func (r *Reader) ReadFloat32() (float32, error) {
var out float32
err := binary.Read(r, r.bo, &out)
return out, err
}
// ReadFloat64 reads a float64 from the underlying *Reader.
func (r *Reader) ReadFloat64() (float64, error) {
var out float64
err := binary.Read(r, r.bo, &out)
return out, err
}
func internalReadString(data []byte, d *encoding.Decoder) (string, error) {
if len(data) == 0 {
return "", nil
}
if d == nil {
// Assume UTF-8
return string(data), nil
}
bytes, err := d.Bytes(data)
if err != nil {
return "", err
}
return string(bytes), nil
}
// ReadString reads a string from the underlying *Reader.
func (r *Reader) ReadString(n uint32) (string, error) {
data := make([]byte, n)
_, err := io.ReadFull(r, data)
if err != nil {
return "", err
}
return internalReadString(data, r.cs.Ideographic)
}
// Skip skips the *Reader ahead by n bytes.
func (r *Reader) Skip(n int64) error {
if r.BytesLeftUntilLimit() < n {
// not enough left to skip
return ErrorInsufficientBytesLeft
}
_, err := io.CopyN(io.Discard, r, n)
return err
}
// PushLimit creates a limit n bytes from the current position.
func (r *Reader) PushLimit(n int64) error {
newLimit := r.bytesRead + n
if newLimit > r.limit && r.limit != LimitReadUntilEOF {
return fmt.Errorf("new limit exceeds current limit of buffer, new limit: %d, limit: %d", newLimit, r.limit)
}
// Add current limit to the stack
r.limitStack = append(r.limitStack, r.limit)
r.limit = newLimit
return nil
}
// PopLimit removes the most recent limit set, and restores the limit before
// that one.
func (r *Reader) PopLimit() error {
// TODO: return an error if trying to Pop the last limit off the slice
last := len(r.limitStack) - 1
if last < 0 {
return ErrorLimitStackEmpty
}
if r.bytesRead < r.limit && r.limit != LimitReadUntilEOF {
// didn't read all the way to the limit, so skip over what's left.
_ = r.Skip(r.limit - r.bytesRead)
}
r.limit = r.limitStack[last]
r.limitStack = r.limitStack[:last]
return nil
}
func (r *Reader) IsLimitExhausted() bool {
// if limit < 0 than we should read until EOF
return (r.BytesLeftUntilLimit() <= 0 && r.limit != LimitReadUntilEOF)
}
func (r *Reader) SetTransferSyntax(bo binary.ByteOrder, implicit bool) {
r.bo = bo
r.implicit = implicit
}
// SetDeflate applies deflate decompression to the underlying *Reader for all
// subsequent reads. This should be set when working with a deflated
// transfer syntax. Right now this is expected to be called once when
// parsing the top level dicom data, and there is no facility to swap
// between deflate and non-deflate reading.
// This also sets the current limit to LimitReadUntilEOF, since the original
// limits (if any) will be based on uncompressed bytes.
func (r *Reader) SetDeflate() {
r.in = bufio.NewReader(flate.NewReader(r.in))
// TODO(https://github.com/suyashkumar/dicom/issues/320): consider always
// having the top level limit read until EOF.
r.limit = LimitReadUntilEOF // needed because original limits may not apply to the deflated *Reader
}
// IsImplicit returns if the currently set transfer syntax on this *Reader is
// implicit or not.
func (r *Reader) IsImplicit() bool { return r.implicit }
// SetCodingSystem sets the charset.CodingSystem to be used when ReadString
// is called.
func (r *Reader) SetCodingSystem(cs charset.CodingSystem) {
r.cs = cs
}
// Peek reads and returns the next n bytes (if possible) without advancing the
// underlying reader.
func (r *Reader) Peek(n int) ([]byte, error) {
return r.in.Peek(n)
}
// ByteOrder returns the current byte order.
func (r *Reader) ByteOrder() binary.ByteOrder {
return r.bo
}