v2 / vlib / io / string_reader / string_reader.v
329 lines · 283 sloc · 8.85 KB · 17b5426813997b3aca12515e580ba09f392333a6
Raw
1@[deprecated: 'If you use it, create a local module named `string_reader`, and copy https://github.com/vlang/v/blob/f24d49259db5d2d7211e79bcf6b6f507d22847f3/vlib/io/string_reader/string_reader.v there.']
2@[deprecated_after: '2026-01-06']
3module string_reader
4
5import io
6import strings
7
8@[params]
9pub struct StringReaderParams {
10pub:
11 // the reader interface
12 reader ?io.Reader
13 // initialize the builder with this source string
14 source ?string
15 // if no source is given the string builder is initialized with this size
16 initial_size int
17}
18
19// StringReader is able to read data from a Reader interface and/or source string to a dynamically
20// growing buffer using a string builder. Unlike the BufferedReader, StringReader will
21// keep the entire contents of the buffer in memory, allowing the incoming data to be reused
22// and read in an efficient matter. The StringReader will not set a maximum capacity to the string
23// builders buffer and could grow very large.
24pub struct StringReader {
25mut:
26 reader ?io.Reader
27 offset int // current offset in the buffer
28pub mut:
29 end_of_stream bool // whether we reached the end of the upstream reader
30 builder strings.Builder
31}
32
33// new creates a new StringReader and sets the string builder size to `initial_size`.
34// If a source
35pub fn StringReader.new(params StringReaderParams) StringReader {
36 mut r := StringReader{
37 reader: params.reader
38 }
39
40 if source := params.source {
41 r.builder = strings.new_builder(source.len)
42 r.builder.write_string(source)
43 if params.reader == none {
44 // There is no upstream reader; only the preloaded buffer can be consumed.
45 r.end_of_stream = true
46 }
47 } else {
48 r.builder = strings.new_builder(params.initial_size)
49 }
50
51 return r
52}
53
54// needs_fill returns whether the buffer needs refilling
55pub fn (r StringReader) needs_fill() bool {
56 return r.offset >= r.builder.len
57}
58
59// needs_fill_until returns whether the buffer needs refilling in order to read
60// `n` bytes
61pub fn (r StringReader) needs_fill_until(n int) bool {
62 return r.offset + n > r.builder.len
63}
64
65// fill_bufer tries to read data into the buffer until either a 0 length read or if read_to_end_of_stream
66// is true then the end of the stream. It returns the number of bytes read
67pub fn (mut r StringReader) fill_buffer(read_till_end_of_stream bool) !int {
68 if r.end_of_stream {
69 return io.Eof{}
70 }
71
72 mut reader := r.reader or { return error('reader is not set') }
73
74 start := r.builder.len
75 mut end := start
76
77 // make sure there is enough room in the string builder
78 unsafe { r.builder.grow_len(io.read_all_len) }
79 defer {
80 // shrink the length of the buffer to the total of bytes read
81 r.builder.go_back(r.builder.len - end)
82 }
83
84 for {
85 read := reader.read(mut r.builder[end..]) or {
86 r.end_of_stream = true
87 break
88 }
89 end += read
90
91 if !read_till_end_of_stream || read == 0 {
92 break
93 } else if r.builder.len == end {
94 unsafe { r.builder.grow_len(io.read_all_grow_len) }
95 }
96 }
97
98 if end == start {
99 return io.Eof{}
100 }
101
102 return end - start
103}
104
105// fill_buffer_until tries read `n` amount of bytes from the reader into the buffer
106// and returns the actual number of bytes read
107pub fn (mut r StringReader) fill_buffer_until(n int) !int {
108 if r.end_of_stream {
109 return io.Eof{}
110 }
111
112 mut reader := r.reader or { return error('reader is not set') }
113
114 start := r.builder.len
115 // make sure there is enough room in the string builder
116 if n > io.read_all_len {
117 unsafe { r.builder.grow_len(io.read_all_len) }
118 } else {
119 unsafe { r.builder.grow_len(n) }
120 }
121
122 mut end := start
123 defer {
124 // shrink the length of the buffer to the total of bytes read
125 r.builder.go_back(r.builder.len - end)
126 }
127 for {
128 read := reader.read(mut r.builder[end..]) or {
129 r.end_of_stream = true
130 break
131 }
132 end += read
133
134 if read == 0 || end - start == n {
135 break
136 } else if r.builder.len == end {
137 remaining := n - (end - start)
138 if remaining > io.read_all_grow_len {
139 unsafe { r.builder.grow_len(io.read_all_grow_len) }
140 } else {
141 unsafe { r.builder.grow_len(remaining) }
142 }
143 }
144 }
145
146 if end == start {
147 return io.Eof{}
148 }
149 return end - start
150}
151
152// read_all_bytes reads all bytes from a reader until either a 0 length read or if read_to_end_of_stream
153// is true then the end of the stream. It returns a copy of the read data
154pub fn (mut r StringReader) read_all_bytes(read_till_end_of_stream bool) ![]u8 {
155 start := r.offset
156 // ignore Eof error from fill buffer
157 r.fill_buffer(read_till_end_of_stream) or {}
158 r.offset = r.builder.len
159 // check if there was still data in the buffer, but the reader has reached its end of stream
160 if start == r.offset {
161 return io.Eof{}
162 }
163
164 return r.get_part(start, r.offset - start)!
165}
166
167// read_all reads all bytes from a reader until either a 0 length read or if read_to_end_of_stream
168// is true then the end of the stream. It produces a string from the read data
169pub fn (mut r StringReader) read_all(read_till_end_of_stream bool) !string {
170 buf := r.read_all_bytes(read_till_end_of_stream)!
171 return unsafe { tos(buf.data, buf.len) }
172}
173
174// read_bytes tries to read n amount of bytes from the reader
175pub fn (mut r StringReader) read_bytes(n int) ![]u8 {
176 start := r.offset
177
178 if r.needs_fill_until(n) {
179 actual_read := r.fill_buffer_until(n - (r.builder.len - r.offset))!
180 r.offset += actual_read
181 } else {
182 r.offset += n
183 }
184
185 return r.get_part(start, r.offset - start)!
186}
187
188// read_bytes tries to read `n` amount of bytes from the reader and produces a string
189// from the read data
190pub fn (mut r StringReader) read_string(n int) !string {
191 buf := r.read_bytes(n)!
192 return unsafe { tos(buf.data, buf.len) }
193}
194
195// read implements the Reader interface
196pub fn (mut r StringReader) read(mut buf []u8) !int {
197 if buf.len == 0 {
198 return 0
199 }
200
201 available := r.builder.len - r.offset
202 if available < buf.len && !r.end_of_stream {
203 if r.reader != none {
204 missing := buf.len - available
205 r.fill_buffer_until(missing) or {
206 if err !is io.Eof {
207 return err
208 }
209 }
210 }
211 }
212
213 available_after_fill := r.builder.len - r.offset
214 if available_after_fill == 0 {
215 if r.reader == none && !r.end_of_stream {
216 return error('reader is not set')
217 }
218 return io.Eof{}
219 }
220
221 read := if available_after_fill < buf.len { available_after_fill } else { buf.len }
222 start := r.offset
223 r.offset += read
224
225 copy(mut buf, r.builder[start..start + read])
226 return read
227}
228
229// read_line attempts to read a line from the reader.
230// It reads until it finds the specified delimiter or the end of stream.
231// The returned string does not include the delimiter.
232// When the delimiter is `\n`, a preceding `\r` is treated as part of CRLF
233// and is also omitted from the returned string.
234@[direct_array_access]
235pub fn (mut r StringReader) read_line(config io.BufferedReadLineConfig) !string {
236 if r.end_of_stream && r.needs_fill() {
237 return io.Eof{}
238 }
239
240 start := r.offset
241 for {
242 if r.needs_fill() {
243 r.fill_buffer(false) or {
244 // we are at the end of the stream
245 if r.offset == start {
246 return io.Eof{}
247 }
248 return r.get_string_part(start, r.offset - start)!
249 }
250 }
251 // try to find a newline character
252 mut i := r.offset
253 for ; i < r.builder.len; i++ {
254 c := r.builder[i]
255 if c == config.delim {
256 // great, we hit something
257 // do some checking for whether we hit \r\n or just \n
258 mut x := i
259 if i > start && config.delim == `\n` && r.builder[i - 1] == `\r` {
260 x--
261 }
262 r.offset = i + 1
263 return r.get_string_part(start, x - start)!
264 }
265 }
266 r.offset = i
267 }
268
269 return io.Eof{}
270}
271
272// write implements the Writer interface
273pub fn (mut r StringReader) write(buf []u8) !int {
274 return r.builder.write(buf)!
275}
276
277// get_data returns a copy of the buffer
278@[inline]
279pub fn (r StringReader) get_data() []u8 {
280 unsafe {
281 mut x := malloc_noscan(r.builder.len)
282 vmemcpy(x, &u8(r.builder.data), r.builder.len)
283 return x.vbytes(r.builder.len)
284 }
285}
286
287// get get_part returns a copy of a part of the buffer from `start` till `start` + `n`
288pub fn (r StringReader) get_part(start int, n int) ![]u8 {
289 if start + n > r.builder.len {
290 return io.Eof{}
291 }
292
293 unsafe {
294 mut x := malloc_noscan(n)
295 vmemcpy(x, &u8(r.builder.data) + start, n)
296 return x.vbytes(n)
297 }
298}
299
300// get_string produces a string from all the bytes in the buffer
301@[inline]
302pub fn (r StringReader) get_string() string {
303 return r.builder.spart(0, r.builder.len)
304}
305
306// get_string_part produces a string from `start` till `start` + `n` of the buffer
307pub fn (r StringReader) get_string_part(start int, n int) !string {
308 if start + n > r.builder.len {
309 return io.Eof{}
310 }
311
312 return r.builder.spart(start, n)
313}
314
315// flush clears the stringbuilder and returns the resulting string and the stringreaders
316// offset is reset to 0
317pub fn (mut r StringReader) flush() string {
318 r.offset = 0
319 return r.builder.str()
320}
321
322// free frees the memory block used for the string builders buffer,
323// a new string builder with size 0 is initialized and the stringreaders offset is reset to 0
324@[unsafe]
325pub fn (mut r StringReader) free() {
326 unsafe { r.builder.free() }
327 r.builder = strings.new_builder(0)
328 r.offset = 0
329}
330