lab.nexedi.com/kirr/go123@v0.0.0-20240207185015-8299741fa871/xbufio/seqbuf_ioat_test.go (about) 1 // Copyright (C) 2017 Nexedi SA and Contributors. 2 // Kirill Smelkov <kirr@nexedi.com> 3 // 4 // This program is free software: you can Use, Study, Modify and Redistribute 5 // it under the terms of the GNU General Public License version 3, or (at your 6 // option) any later version, as published by the Free Software Foundation. 7 // 8 // You can also Link and Combine this program with other software covered by 9 // the terms of any of the Free Software licenses or any of the Open Source 10 // Initiative approved licenses and Convey the resulting work. Corresponding 11 // source of such a combination shall include the source code for all other 12 // software used. 13 // 14 // This program is distributed WITHOUT ANY WARRANTY; without even the implied 15 // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 16 // 17 // See COPYING file for full licensing terms. 18 // See https://www.nexedi.com/licensing for rationale and options. 19 20 package xbufio 21 22 import ( 23 "bytes" 24 "errors" 25 "io" 26 "testing" 27 ) 28 29 30 // XReader is an io.ReaderAt that reads first 256 bytes with content_i = i. 31 // bytes in range [100, 104] give EIO on reading. 32 type XReader struct { 33 } 34 35 var EIO = errors.New("input/output error") 36 37 func (r *XReader) ReadAt(p []byte, pos int64) (n int, err error) { 38 for n < len(p) && pos < 0x100 { 39 if 100 <= pos && pos <= 104 { 40 err = EIO 41 break 42 } 43 44 p[n] = byte(pos) 45 n++ 46 pos++ 47 } 48 49 if n < len(p) && err == nil { 50 err = io.EOF 51 } 52 53 return n, err 54 } 55 56 57 // read @pos/len -> rb.pos, len(rb.buf) 58 var xSeqBufTestv = []struct {pos int64; Len int; bufPos int64; bufLen int} { 59 {40, 5, 40, 10}, // 1st access, forward by default 60 {45, 7, 50, 10}, // part taken from buf, part read next, forward (trend) 61 {52, 5, 50, 10}, // everything taken from buf 62 {57, 5, 60, 10}, // part taken from buf, part read next (trend) 63 {60, 11, 60, 10}, // access > cap(buf), buf skipped 64 {71, 11, 60, 10}, // access > cap(buf), once again 65 {82, 10, 82, 10}, // access = cap(buf), should refill buf 66 {92, 5, 92, 8}, // next access - should refill buffer (trend), but only up to EIO range 67 {97, 4, 100, 0}, // this triggers user-visible EIO, buffer scratched 68 {101, 5, 101, 0}, // EIO again 69 {105, 5, 105, 10}, // past EIO range - buffer refilled 70 {88, 8, 88, 10}, // go back again a bit before EIO range 71 {96, 6, 98, 2}, // this uses both buffered data + result of next read which hits EIO 72 {110,70, 98, 2}, // very big access forward, buf untouched 73 {180,70, 98, 2}, // big access ~ forward 74 75 {170, 5, 170, 10}, // backward: buffer refilled forward because prev backward reading was below 76 {168, 4, 160, 10}, // backward: buffer refilled backward 77 {162, 6, 160, 10}, // backward: all data read from buffer 78 {150,12, 160, 10}, // big backward: buf untouched 79 {142, 6, 140, 10}, // backward: buffer refilled up to posLastIO 80 {130,12, 140, 10}, // big backward: buf untouched 81 {122, 9, 121, 10}, // backward overlapping with last bigio: buf correctly refilled 82 {131, 9, 131, 10}, // forward after backward: buf refilled forward 83 {122, 6, 121, 10}, // backward after forward: buf refilled backward 84 {131, 9, 131, 10}, // forward again 85 {136,20, 131, 10}, // big forward starting from inside filled buf 86 {128, 4, 126, 10}, // backward (not trend): buf refilled up to posLastIO 87 88 // interleaved backward + fwd-fwd-fwd 89 {200,10, 200, 10}, // reset @200 90 {194, 1, 190, 10}, // 1st backward access: buf refilled 91 {186, 1, 184, 10}, // trendy backward access - buf refilled up-to prev back read 92 93 {187, 1, 184, 10}, // fwd-fwd-fwd (all already buffered) 94 {188, 2, 184, 10}, 95 {190, 3, 184, 10}, 96 97 {182, 4, 174, 10}, // trendy backward access - part taken from buffer and buf refilled adjacent to previous backward IO 98 99 {168, 1, 168, 10}, // trendy backward access farther than cap(buf) - buf refilled right at @pos 100 101 {169, 7, 168, 10}, // fwd-fwd-fwd (partly buffered / partly loaded) 102 {176, 3, 178, 10}, 103 {179, 6, 178, 10}, 104 105 // interleaved forward + back-back-back 106 {200,10, 200, 10}, // reset @200 107 {206, 1, 200, 10}, // 1st forward access 108 {214, 1, 207, 10}, // trendy forward access - buf refilled adjacent to previous forward read 109 110 {213, 1, 207, 10}, // back-back-back (all already buffered) 111 {211, 2, 207, 10}, 112 {207, 5, 207, 10}, 113 114 {215, 4, 217, 10}, // trendy forward access - part taken from buffer and buf refilled adjacent to previous forward IO 115 116 {235, 1, 235, 10}, // trendy forward access farther than cap(buf) - buf refilled right at @pos 117 118 {234, 1, 225, 10}, // back-back-back (partly loaded / then partly buffered) 119 {230, 3, 225, 10}, 120 {222, 8, 215, 10}, 121 {219, 3, 215, 10}, 122 123 // backward (non trend) vs not overlapping previous forward 124 {230,10, 230, 10}, // forward @230 (1) 125 {220,10, 220, 10}, // backward @220 (2) 126 {250, 4, 250, 6}, // forward @250 (3) 127 {245, 5, 240, 10}, // backward @245 (4) 128 129 {5, 4, 5, 10}, // forward near file start 130 {2, 3, 0, 10}, // backward: buf does not go beyond 0 131 132 {40, 0, 0, 10}, // zero-sized out-of-buffer read do not change buffer 133 134 // backward (not trend) vs EIO 135 {108,10, 108, 10}, // reset @108 136 { 98, 1, 98, 2}, // backward not overlapping EIO: buf filled < EIO range 137 {108,10, 108, 10}, // reset @108 138 { 99, 4, 98, 2}, // backward overlapping head EIO: buf filled < EIO range, EIO -> user 139 {108,10, 108, 10}, // reset @108 140 { 99, 6, 98, 2}, // backward overlapping whole EIO range: buf filled <= EIO range, EIO -> user 141 {108,10, 108, 10}, // reset @108 142 {100, 4, 98, 2}, // backward = EIO range: buf filled < EIO range, EIO -> user 143 {110,10, 110, 10}, // reset @110 144 {101, 2, 100, 0}, // backward inside EIO range: buf scratched, EIO -> user 145 {110,10, 110, 10}, // reset @110 146 {103, 5, 100, 0}, // backward overlapping tail EIO: buf scratched, EIO -> user 147 {110,10, 110, 10}, // reset state: forward @110 148 {105, 7, 100, 0}, // backward client after EIO: buf scratched but read request satisfied 149 150 // backward (trend) vs EIO 151 // NOTE this is reverse of `backward (not trend) vs EIO 152 {110,10, 110, 10}, // reset state: forward @110 153 {105, 7, 100, 0}, // backward client after EIO: buf scratched but read request satisfied 154 {110,10, 110, 10}, // reset @110 155 {103, 5, 98, 2}, // backward overlapping tail EIO: buf scratched (XXX), EIO -> user 156 {110,10, 110, 10}, // reset @110 157 {101, 2, 93, 7}, // backward inside EIO range: buf scratched (XXX), EIO -> user 158 {108,10, 108, 10}, // reset @108 159 {100, 4, 94, 6}, // backward = EIO range: buf filled < EIO range, EIO -> user 160 {108,10, 108, 10}, // reset @108 161 { 99, 6, 95, 5}, // backward overlapping whole EIO range: buf filled <= EIO range, EIO -> user 162 {108,10, 108, 10}, // reset @108 163 { 99, 4, 98, 2}, // backward overlapping head EIO: buf filled < EIO range, EIO -> user 164 {108,10, 108, 10}, // reset @108 165 { 98, 1, 89, 10}, // backward not overlapping EIO: buf filled according to backward trend 166 167 // forward (trend) vs EIO 168 { 0, 1, 0, 10}, 169 { 88,10, 88, 10}, // reset forward @98 170 { 98, 1, 98, 2}, // forward not overlapping EIO: buf filled < EIO range 171 { 0, 1, 0, 10}, 172 { 88,10, 88, 10}, // reset forward @98 173 { 99, 4, 98, 2}, // forward overlapping head EIO: buf filled < EIO range, EIO -> user 174 { 0, 1, 0, 10}, 175 { 88,10, 88, 10}, // reset forward @98 176 { 99, 6, 98, 2}, // forward overlapping whole EIO range: buf filled <= EIO range, EIO -> user 177 { 0, 1, 0, 10}, 178 { 88,10, 88, 10}, // reset forward @98 179 {100, 4, 98, 2}, // forward = EIO range: buf filled < EIO range, EIO -> user 180 { 0, 1, 0, 10}, 181 { 90,10, 90, 10}, // reset forward @100 182 {101, 2, 100, 0}, // forward inside EIO range: buf scratched, EIO -> user 183 { 0, 1, 0, 10}, 184 { 90,10, 90, 10}, // reset forward @100 185 {103, 5, 100, 0}, // forward overlapping tail EIO: buf scratched, EIO -> user 186 { 0, 1, 0, 10}, 187 { 90,10, 90, 10}, // reset forward @100 188 {105, 2, 100, 0}, // forward client after EIO: buf scratched but read request satisfied 189 190 { 0, 1, 0, 10}, 191 { 90, 5, 90, 10}, // reset forward @95 192 { 99, 3, 96, 4}, // forward jump client overlapping head EIO: buf filled < EIO range, EIO -> user 193 194 { 0, 1, 0, 10}, 195 { 89, 5, 89, 10}, // reset forward @94 196 { 98, 2, 95, 5}, // forward jump client reading < EIO: buf filled < EIO range, user request satisfied 197 198 199 // EOF handling 200 {250, 4, 250, 6}, // access near EOF - buffer fill hits EOF, but not returns it to client 201 {254, 5, 256, 0}, // access overlapping EOF - EOF returned, buf scratched 202 {256, 1, 256, 0}, // access past EOF -> EOF 203 {257, 1, 257, 0}, // ----//---- 204 205 // forward with jumps - buffer is still refilled adjacent to previous reading 206 // ( because jumps are not sequential access and we are optimizing for sequential cases. 207 // also: if jump > cap(buf) reading will not be adjacent) 208 { 0, 1, 0, 10}, // reset 209 { 0, 5, 0, 10}, 210 { 9, 3, 6, 10}, 211 {20, 3, 20, 10}, 212 } 213 214 215 func TestSeqReaderAt(t *testing.T) { 216 r := &XReader{} 217 rb := NewSeqReaderAtSize(r, 10) // with 10 it is easier to do/check math for a human 218 219 for _, tt := range xSeqBufTestv { 220 pOk := make([]byte, tt.Len) 221 pB := make([]byte, tt.Len) 222 223 nOk, errOk := r.ReadAt(pOk, tt.pos) 224 nB, errB := rb.ReadAt(pB, tt.pos) 225 226 pOk = pOk[:nOk] 227 pB = pB[:nB] 228 229 // check that reading result is the same 230 if !(bytes.Equal(pB, pOk) && errB == errOk) { 231 t.Fatalf("%v: -> %v, %#v ; want %v, %#v", tt, pB, errB, pOk, errOk) 232 } 233 234 // verify buffer state 235 if !(rb.pos == tt.bufPos && len(rb.buf) == tt.bufLen) { 236 t.Fatalf("%v: -> unexpected buffer state @%v #%v", tt, rb.pos, len(rb.buf)) 237 } 238 } 239 } 240 241 // this is benchmark for how thin wrapper is, not for logic inside it 242 func BenchmarkSeqReaderAt(b *testing.B) { 243 r := &XReader{} 244 rb := NewSeqReaderAtSize(r, 10) // same as in TestSeqReaderAt 245 buf := make([]byte, 128 /* > all .Len in xSeqBufTestv */) 246 247 for i := 0; i < b.N; i++ { 248 for _, tt := range xSeqBufTestv { 249 rb.ReadAt(buf[:tt.Len], tt.pos) 250 } 251 } 252 }