@@ -32,3 +32,120 @@ func TestOpen(t *testing.T) {
32
32
t .Fatalf ("\n got %q\n want %q" , string (got ), string (want ))
33
33
}
34
34
}
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