mirror of
https://github.com/vlang/v.git
synced 2025-09-13 14:32:26 +03:00
io: add a BufferedWriter and supporting methods (#22265)
This commit is contained in:
parent
295807d7ef
commit
64d1770cf1
2 changed files with 220 additions and 0 deletions
87
vlib/io/buffered_writer.v
Normal file
87
vlib/io/buffered_writer.v
Normal file
|
@ -0,0 +1,87 @@
|
|||
module io
|
||||
|
||||
pub struct BufferedWriter {
|
||||
mut:
|
||||
n int
|
||||
wr Writer
|
||||
pub mut:
|
||||
buf []u8
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct BufferedWriterConfig {
|
||||
pub:
|
||||
writer Writer
|
||||
cap int = 128 * 1024
|
||||
}
|
||||
|
||||
// new_buffered_writer creates a new BufferedWriter with the specified BufferedWriterConfig.
|
||||
// Returns an error when cap is 0 or negative.
|
||||
pub fn new_buffered_writer(o BufferedWriterConfig) !&BufferedWriter {
|
||||
if o.cap < 1 {
|
||||
return error('`o.cap` must be a positive integer')
|
||||
}
|
||||
|
||||
return &BufferedWriter{
|
||||
buf: []u8{len: o.cap}
|
||||
wr: o.writer
|
||||
}
|
||||
}
|
||||
|
||||
// reset resets the buffer to its initial state.
|
||||
pub fn (mut b BufferedWriter) reset() {
|
||||
cap := b.buf.len
|
||||
b.buf = []u8{len: cap}
|
||||
b.n = 0
|
||||
}
|
||||
|
||||
// buffered returns the number of bytes currently stored in the buffer.
|
||||
pub fn (b BufferedWriter) buffered() int {
|
||||
return b.n
|
||||
}
|
||||
|
||||
// flush writes the buffered data to the underlying writer and clears the buffer, ensures all data is
|
||||
// written.
|
||||
// Returns an error if the writer fails to write all buffered data.
|
||||
pub fn (mut b BufferedWriter) flush() ! {
|
||||
if b.buffered() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
n := b.wr.write(b.buf[0..b.n])!
|
||||
if n < b.n {
|
||||
return error('Writer accepted less bytes than expected without returning any explicit error.')
|
||||
}
|
||||
|
||||
b.n = 0
|
||||
return
|
||||
}
|
||||
|
||||
// available returns the amount of available space left in the buffer.
|
||||
pub fn (b BufferedWriter) available() int {
|
||||
return b.buf.len - b.n
|
||||
}
|
||||
|
||||
// write writes `src` in the buffer, flushing it to the underlying writer as needed, and returns the
|
||||
// number of bytes written.
|
||||
pub fn (mut b BufferedWriter) write(src []u8) !int {
|
||||
mut p := src.clone()
|
||||
mut nn := 0
|
||||
for p.len > b.available() {
|
||||
mut n := 0
|
||||
if b.buffered() == 0 {
|
||||
n = b.wr.write(p)!
|
||||
} else {
|
||||
n = copy(mut b.buf[b.n..], p)
|
||||
b.n += n
|
||||
b.flush()!
|
||||
}
|
||||
nn += n
|
||||
p = p[n..].clone()
|
||||
}
|
||||
|
||||
n := copy(mut b.buf[b.n..], p)
|
||||
b.n += n
|
||||
nn += n
|
||||
return nn
|
||||
}
|
133
vlib/io/buffered_writer_test.v
Normal file
133
vlib/io/buffered_writer_test.v
Normal file
|
@ -0,0 +1,133 @@
|
|||
import io
|
||||
import rand
|
||||
|
||||
struct ArrayWriter {
|
||||
pub mut:
|
||||
result []u8
|
||||
}
|
||||
|
||||
// implement io.Writer
|
||||
fn (mut aw ArrayWriter) write(buf []u8) !int {
|
||||
len := buf.len
|
||||
mut res := 0
|
||||
for i := 0; i < len; i++ {
|
||||
aw.result << buf[i]
|
||||
res++
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// write less than max bytes, returns number of bytes written
|
||||
// data is written in chunks.
|
||||
fn write_random_data(mut aw ArrayWriter, mut bw io.BufferedWriter, max int) !int {
|
||||
less_than_max := max - 255 // guarantee 1 full u8 less than max
|
||||
mut total := 0
|
||||
for total < less_than_max {
|
||||
r := rand.u8()
|
||||
d := rand.bytes(r)!
|
||||
w := bw.write(d)!
|
||||
total += w
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
fn test_flush() {
|
||||
mut aw := ArrayWriter{}
|
||||
max := 65536
|
||||
mut bw := io.new_buffered_writer(writer: aw, cap: max)!
|
||||
|
||||
// write less data than buffer capacity, the underlying writer should receive no data.
|
||||
written := write_random_data(mut aw, mut bw, 65536)!
|
||||
assert written == bw.buffered()
|
||||
assert aw.result.len == 0
|
||||
|
||||
bw.flush()!
|
||||
assert aw.result.len == written
|
||||
assert bw.buffered() == 0
|
||||
assert bw.available() == max
|
||||
}
|
||||
|
||||
fn test_write() {
|
||||
mut aw := ArrayWriter{}
|
||||
max := 65536
|
||||
mut bw := io.new_buffered_writer(writer: aw, cap: max)!
|
||||
|
||||
// write less data than buffer capacity, the underlying writer should receive no data.
|
||||
written := write_random_data(mut aw, mut bw, 65536)!
|
||||
|
||||
// now exceed buffer capacity by a little
|
||||
little := rand.u8()
|
||||
excess := bw.available() + little
|
||||
excess_data := rand.bytes(excess)!
|
||||
w := bw.write(excess_data)!
|
||||
assert bw.buffered() == little
|
||||
assert bw.available() == max - little
|
||||
assert aw.result.len == max
|
||||
}
|
||||
|
||||
fn test_write_big() {
|
||||
mut aw := ArrayWriter{}
|
||||
max := 65536
|
||||
mut bw := io.new_buffered_writer(writer: aw, cap: max)!
|
||||
|
||||
more_than_max := max + rand.u8()
|
||||
big_source := rand.bytes(more_than_max)!
|
||||
w := bw.write(big_source)!
|
||||
assert w == more_than_max
|
||||
assert bw.buffered() == 0
|
||||
assert bw.available() == max
|
||||
assert aw.result.len == more_than_max
|
||||
}
|
||||
|
||||
// create_data returns an array with `n` elements plus `\n` as last character.
|
||||
fn create_data(n int) []u8 {
|
||||
mut res := []u8{}
|
||||
for i := 0; i < n; i++ {
|
||||
res << `X`
|
||||
}
|
||||
res << `\n`
|
||||
return res
|
||||
}
|
||||
|
||||
fn test_simple_write() {
|
||||
mut aw := ArrayWriter{}
|
||||
mut bw := io.new_buffered_writer(writer: aw, cap: 10)!
|
||||
mut data := create_data(6)
|
||||
w1 := bw.write(data)!
|
||||
|
||||
// add data to buffer, less than cap
|
||||
// data is written to buffer, not to underlying writer.
|
||||
assert w1 == 7 // 6*x + \n
|
||||
assert bw.buffered() == 7
|
||||
assert aw.result.len == 0
|
||||
|
||||
// add more data, exceed cap
|
||||
// data is flushed to underlying writer when buffer is full, then more data is written to buffer.
|
||||
w2 := bw.write(data)!
|
||||
assert w2 == 7
|
||||
assert bw.buffered() == 4
|
||||
assert aw.result.len == 10
|
||||
|
||||
// exceed cap immediately
|
||||
// all data is written without buffering
|
||||
aw.result = []u8{}
|
||||
data = create_data(33)
|
||||
bw.reset()
|
||||
w3 := bw.write(data)!
|
||||
assert bw.buffered() == 0
|
||||
assert aw.result.len == 34 // 33*x + \n
|
||||
}
|
||||
|
||||
fn test_simple_flush() {
|
||||
mut aw := ArrayWriter{}
|
||||
mut bw := io.new_buffered_writer(writer: aw, cap: 10)!
|
||||
data := create_data(6)
|
||||
w := bw.write(data)!
|
||||
|
||||
assert w == 7 // 6*x + \n
|
||||
assert bw.buffered() == 7
|
||||
|
||||
bw.flush()!
|
||||
assert bw.buffered() == 0
|
||||
assert aw.result.len == 7
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue