Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(168)

Side by Side Diff: common/archive/ar/reader.go

Issue 2014243002: WIP: Archive command which is much faster (Closed) Base URL: https://github.com/luci/luci-go@master
Patch Set: Fixes. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file.
4
5 // Read an ar file with BSD formatted file names.
6
7 package ar
8
9 import (
10 "bytes"
11 "errors"
12 "fmt"
13 "io"
14 "os"
15 "time"
16 )
17
18 type arFileInfo struct {
19 // os.FileInfo parts
20 name string
21 size int64
22 mode uint32
23 modtime uint64
24 // Extra parts
25 uid int
26 gid int
27 }
28
29 // os.FileInfo interface
30 func (fi *arFileInfo) Name() string { return fi.name }
31 func (fi *arFileInfo) Size() int64 { return fi.size }
32 func (fi *arFileInfo) Mode() os.FileMode { return os.FileMode(fi.mode) }
33 func (fi *arFileInfo) ModTime() time.Time { return time.Unix(int64(fi.modtime), 0) }
34 func (fi *arFileInfo) IsDir() bool { return fi.Mode().IsDir() }
35 func (fi *arFileInfo) Sys() interface{} { return fi }
36
37 // Extra
38 func (fi *arFileInfo) UserId() int { return fi.uid }
39 func (fi *arFileInfo) GroupId() int { return fi.gid }
40
41 var (
42 ErrHeader = errors.New("archive/ar: invalid ar header")
43 )
44
45 type ReaderStage uint
46
47 const (
48 READ_HEADER ReaderStage = iota
49 READ_BODY
50 READ_CLOSED
51 )
52
53 type Reader struct {
54 stage ReaderStage
55 r io.Reader
56 bytesrequired int64
57 needspadding bool
58 }
59
60 func NewReader(r io.Reader) (*Reader, error) {
61 reader := Reader{r: r, bytesrequired: 0, needspadding: false}
62 err := reader.checkBytes("header", []byte("!<arch>\n"))
63 if err != nil {
64 return nil, err
65 } else {
66 return &reader, nil
67 }
68 }
69
70 func (ar *Reader) checkBytes(name string, str []byte) error {
71 buffer := make([]byte, len(str))
72
73 count, err := io.ReadFull(ar.r, buffer)
74 if err != nil {
75 return err
76 }
77
78 if count != len(buffer) {
79 return errors.New(fmt.Sprintf("%s: Not enough data read (only %d , needed %d)", name, count, len(buffer)))
80 }
81
82 if bytes.Equal(str, buffer) {
83 return nil
84 } else {
85 return errors.New(fmt.Sprintf("%s: error in bytes (wanted: %v go t: %v)", name, buffer, str))
86 }
87 }
88
89 func (ar *Reader) Close() error {
90 switch ar.stage {
91 case READ_HEADER:
92 // Good
93 case READ_BODY:
94 return errors.New("Usage error, reading a file.")
95 case READ_CLOSED:
96 return errors.New("Usage error, archive already closed.")
97 default:
98 panic(fmt.Sprintf("Unknown reader mode: %d", ar.stage))
99 }
100 //ar.r.Close()
101 ar.stage = READ_CLOSED
102 return nil
103 }
104
105 func (ar *Reader) readBytes(numbytes int64) error {
106 if numbytes > ar.bytesrequired {
107 return errors.New(fmt.Sprintf("To much data read! Needed %d, got %d", ar.bytesrequired, numbytes))
108 }
109
110 ar.bytesrequired -= numbytes
111 if ar.bytesrequired != 0 {
112 return nil
113 }
114
115 // Padding to 16bit boundary
116 if ar.needspadding {
117 err := ar.checkBytes("padding", []byte("\n"))
118 if err != nil {
119 return err
120 }
121 ar.needspadding = false
122 }
123 ar.stage = READ_HEADER
124 return nil
125 }
126
127 // Check you can write bytes to the ar at this moment.
128 func (ar *Reader) checkRead() error {
129 switch ar.stage {
130 case READ_HEADER:
131 return errors.New("Usage error, need to read header first.")
132 // Good
133 case READ_BODY:
134 return nil
135 case READ_CLOSED:
136 return errors.New("Usage error, archive closed.")
137 default:
138 panic(fmt.Sprintf("Unknown reader mode: %d", ar.stage))
139 }
140 }
141
142 // Check we have finished writing bytes
143 func (ar *Reader) checkFinished() error {
144 if ar.bytesrequired != 0 {
145 return errors.New(fmt.Sprintf("Didn't read enough bytes %d still needed!", ar.bytesrequired))
146 }
147 return nil
148 }
149
150 func (ar *Reader) readPartial(name string, data []byte) error {
151 err := ar.checkRead()
152 if err != nil {
153 return err
154 }
155
156 datalen := int64(len(data))
157 if datalen > ar.bytesrequired {
158 return errors.New(fmt.Sprintf("To much data! Wanted %d, but had %d", ar.bytesrequired, datalen))
159 }
160
161 count, err := io.ReadFull(ar.r, data)
162 ar.readBytes(int64(count))
163 return nil
164 }
165
166 func (ar *Reader) readHeaderBytes(name string, bytes int, formatstr string) (int 64, error) {
167 data := make([]byte, bytes)
168 _, err := io.ReadFull(ar.r, data)
169 if err != nil {
170 return -1, err
171 }
172
173 var output int64 = 0
174 _, err = fmt.Sscanf(string(data), formatstr, &output)
175 if err != nil {
176 return -1, err
177 }
178
179 if output <= 0 {
180 return -1, errors.New(fmt.Sprintf("%s: bad value %d", name, outp ut))
181 }
182 return output, nil
183 }
184
185 func (ar *Reader) readHeader() (*arFileInfo, error) {
186 switch ar.stage {
187 case READ_HEADER:
188 // Good
189 case READ_BODY:
190 return nil, errors.New("Usage error, already writing a file.")
191 case READ_CLOSED:
192 return nil, errors.New("Usage error, archive closed.")
193 default:
194 panic(fmt.Sprintf("Unknown writer mode: %d", ar.stage))
195 }
196
197 var fi arFileInfo
198
199 // File name length prefixed with '#1/' (BSD variant), 16 bytes
200 namelen, err := ar.readHeaderBytes("filename length", 16, "#1/%13d")
201 if err != nil {
202 return nil, err
203 }
204
205 // Modtime, 12 bytes
206 modtime, err := ar.readHeaderBytes("modtime", 12, "%12d")
207 if err != nil {
208 return nil, err
209 }
210 fi.modtime = uint64(modtime)
211
212 // Owner ID, 6 bytes
213 ownerid, err := ar.readHeaderBytes("ownerid", 6, "%6d")
214 if err != nil {
215 return nil, err
216 }
217 fi.uid = int(ownerid)
218
219 // Group ID, 6 bytes
220 groupid, err := ar.readHeaderBytes("groupid", 6, "%6d")
221 if err != nil {
222 return nil, err
223 }
224 fi.gid = int(groupid)
225
226 // File mode, 8 bytes
227 filemod, err := ar.readHeaderBytes("groupid", 8, "%8o")
228 if err != nil {
229 return nil, err
230 }
231 fi.mode = uint32(filemod)
232
233 // File size, 10 bytes
234 size, err := ar.readHeaderBytes("datasize", 10, "%10d")
235 if err != nil {
236 return nil, err
237 }
238 fi.size = size - namelen
239
240 ar.stage = READ_BODY
241 ar.bytesrequired = size
242 ar.needspadding = (ar.bytesrequired%2 != 0)
243
244 // File magic, 2 bytes
245 err = ar.checkBytes("filemagic", []byte("\x60\n"))
246 if err != nil {
247 return nil, err
248 }
249
250 // Filename - BSD variant
251 filename := make([]byte, namelen)
252 err = ar.readPartial("filename", filename)
253 if err != nil {
254 return nil, err
255 }
256 fi.name = string(filename)
257
258 return &fi, nil
259 }
260
261 func (ar *Reader) Read(b []byte) (n int, err error) {
262 err = ar.readPartial("data", b)
263 if err != nil {
264 return -1, err
265 }
266 err = ar.checkFinished()
267 if err != nil {
268 return -1, err
269 }
270 return len(b), nil
271 }
272 func (ar *Reader) Next() (*arFileInfo, error) {
273 return ar.readHeader()
274 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698