@@ -32,3 +32,120 @@ func TestOpen(t *testing.T) {
3232 t .Fatalf ("\n got %q\n want %q" , string (got ), string (want ))
3333 }
3434}
35+
36+ func TestSeekRead (t * testing.T ) {
37+ const filename = "mmap_test.go"
38+ r , err := Open (filename )
39+ if err != nil {
40+ t .Fatalf ("Open: %v" , err )
41+ }
42+ buf := make ([]byte , 1 )
43+ if _ , err := r .Seek (0 , io .SeekStart ); err != nil {
44+ t .Fatalf ("Seek: %v" , err )
45+ }
46+ n , err := r .Read (buf )
47+ if err != nil {
48+ t .Fatalf ("Read: %v" , err )
49+ }
50+ if n != 1 {
51+ t .Fatalf ("Read: got %d bytes, want 1" , n )
52+ }
53+ if buf [0 ] != '/' { // first comment slash
54+ t .Fatalf ("Read: got %q, want '/'" , buf [0 ])
55+ }
56+ if _ , err := r .Seek (1 , io .SeekCurrent ); err != nil {
57+ t .Fatalf ("Seek: %v" , err )
58+ }
59+ n , err = r .Read (buf )
60+ if err != nil {
61+ t .Fatalf ("Read: %v" , err )
62+ }
63+ if n != 1 {
64+ t .Fatalf ("Read: got %d bytes, want 1" , n )
65+ }
66+ if buf [0 ] != ' ' { // space after comment
67+ t .Fatalf ("Read: got %q, want ' '" , buf [0 ])
68+ }
69+ if _ , err := r .Seek (- 1 , io .SeekEnd ); err != nil {
70+ t .Fatalf ("Seek: %v" , err )
71+ }
72+ n , err = r .Read (buf )
73+ if err != nil {
74+ t .Fatalf ("Read: %v" , err )
75+ }
76+ if n != 1 {
77+ t .Fatalf ("Read: got %d bytes, want 1" , n )
78+ }
79+ if buf [0 ] != '\n' { // last newline
80+ t .Fatalf ("Read: got %q, want newline" , buf [0 ])
81+ }
82+ if _ , err := r .Seek (0 , io .SeekEnd ); err != nil {
83+ t .Fatalf ("Seek: %v" , err )
84+ }
85+ if _ , err := r .Read (buf ); err != io .EOF {
86+ t .Fatalf ("Read: expected EOF, got %v" , err )
87+ }
88+ }
89+
90+ func TestWriterTo_idempotency (t * testing.T ) {
91+ const filename = "mmap_test.go"
92+ r , err := Open (filename )
93+ if err != nil {
94+ t .Fatalf ("Open: %v" , err )
95+ }
96+ buf := bytes .NewBuffer (make ([]byte , 0 , len (r .data )))
97+ // first run
98+ n , err := r .WriteTo (buf )
99+ if err != nil {
100+ t .Fatalf ("WriteTo: %v" , err )
101+ }
102+ if n != int64 (len (r .data )) {
103+ t .Fatalf ("WriteTo: got %d bytes, want %d" , n , len (r .data ))
104+ }
105+ if ! bytes .Equal (buf .Bytes (), r .data ) {
106+ t .Fatalf ("WriteTo: got %q, want %q" , buf .Bytes (), r .data )
107+ }
108+ // second run
109+ n , err = r .WriteTo (buf )
110+ if err != nil {
111+ t .Fatalf ("WriteTo: %v" , err )
112+ }
113+ if n != 0 {
114+ t .Fatalf ("WriteTo: got %d bytes, want %d" , n , 0 )
115+ }
116+ if ! bytes .Equal (buf .Bytes (), r .data ) {
117+ t .Fatalf ("WriteTo: got %q, want %q" , buf .Bytes (), r .data )
118+ }
119+ }
120+
121+ func BenchmarkMmapCopy (b * testing.B ) {
122+ var f io.ReadSeeker
123+
124+ // mmap some big-ish file; will only work on unix-like OSs.
125+ r , err := Open ("/proc/self/exe" )
126+ if err != nil {
127+ b .Fatalf ("Open: %v" , err )
128+ }
129+
130+ // Sanity check: ensure we will run into the io.Copy optimization when using the NEW code above.
131+ var _ io.WriterTo = r
132+
133+ // f = io.NewSectionReader(r, 0, int64(len(r.data))) // old
134+ f = r // new
135+
136+ buf := bytes .NewBuffer (make ([]byte , 0 , len (r .data )))
137+ // "Hide" the ReaderFrom interface by wrapping the writer.
138+ // Otherwise we skew the results by optimizing the wrong side.
139+ writer := struct { io.Writer }{buf }
140+
141+ b .ReportAllocs ()
142+ b .ResetTimer ()
143+
144+ for i := 0 ; i < b .N ; i ++ {
145+ _ , _ = f .Seek (0 , io .SeekStart )
146+ buf .Reset ()
147+
148+ n , _ := io .Copy (writer , f )
149+ b .SetBytes (n )
150+ }
151+ }
0 commit comments