21
21
ReadSlice(delim byte) (line []byte, err os.Error)
24
func readPrelude(r ReadSlicer) (info headerInfo, err os.Error) {
24
func readPrelude(r ReadSlicer) (info headerInfo) {
25
25
headerLine, err := r.ReadSlice('\n')
29
29
if !bytes.Equal(headerLine, []byte(HEADER_FORMAT_3)) {
30
err = ParseError(fmt.Sprintf("Invalid header\n"+
30
panic(ParseError(fmt.Sprintf("Invalid header\n"+
33
HEADER_FORMAT_3, headerLine))
33
HEADER_FORMAT_3, headerLine)))
36
35
line, err := r.ReadSlice('\n')
38
err = ParseError(fmt.Sprintf("Unable to read crc32 line: %s",
37
panic(ParseError(fmt.Sprintf("Unable to read crc32 line: %s", err)))
42
39
if !bytes.Equal(line[0:7], []byte("crc32: ")) {
43
err = ParseError(fmt.Sprintf("Invalid 'crc32' line. Found: %s", line))
40
panic(ParseError(fmt.Sprintf("Invalid 'crc32' line. Found: %s", line)))
46
42
// I miss line[5:-1] syntax...
47
43
info.Crc32, err = strconv.Atoui(string(line[7 : len(line)-1]))
49
err = ParseError(fmt.Sprintf("Invalid crc32: %s", err))
45
panic(ParseError(fmt.Sprintf("Invalid crc32: %s", err)))
52
47
line, err = r.ReadSlice('\n')
54
err = ParseError(fmt.Sprintf("Unable to read num_entries line: %s",
49
panic(ParseError(fmt.Sprintf("Unable to read num_entries line: %s",
58
52
if !bytes.Equal(line[0:13], []byte("num_entries: ")) {
59
err = ParseError(fmt.Sprintf("Invalid 'num_entries' line. Found: %s",
53
panic(ParseError(fmt.Sprintf("Invalid 'num_entries' line. Found: %s",
63
56
info.NumEntries, err = strconv.Atoui(string(line[13 : len(line)-1]))
65
err = ParseError(fmt.Sprintf("Invalid num_entries: %s", err))
58
panic(ParseError(fmt.Sprintf("Invalid num_entries: %s", err)))
84
77
// Ideally this would be a hidden func, but it makes testing harder, so I'm
86
func ParseLenDelimitedLine(line []byte) (result []string, err os.Error) {
78
// making it exported. Because this is not meant to be exported, it uses
79
// "panic" for its error handlinng.
80
func ParseLenDelimitedLine(line []byte) (result []string) {
87
81
if len(line) < 3 { // must have a value and a newline
88
err = ParseError("Invalid len-delimited line: too short")
82
panic(ParseError("Invalid len-delimited line: too short"))
91
84
chunks := bytes.Split(line[:len(line)-2], []byte{0}, -1)
92
85
num, err := strconv.Atoi(string(chunks[0]))
94
err = ParseError(fmt.Sprintf("Invalid len-delimited line: %s", err))
87
panic(ParseError(fmt.Sprintf("Invalid len-delimited line: %s", err)))
97
89
result = make([]string, 0, num)
98
90
for _, x := range chunks[1:] {
99
91
result = append(result, string(x))
101
93
if num != len(result) {
102
err = ParseError(fmt.Sprintf(
94
panic(ParseError(fmt.Sprintf(
103
95
"Invalid len-delimited line: Expected %d entries, found %d",
110
func readParentsGhosts(r *bufio.Reader) (parents, ghosts []string, err os.Error) {
102
func readParentsGhosts(r *bufio.Reader) (parents, ghosts []string) {
111
103
line, err := r.ReadSlice('\n')
113
err = ParseError(fmt.Sprintf("Unable to read parents line: %s", err))
116
parents, err = ParseLenDelimitedLine(line)
105
panic(ParseError(fmt.Sprintf("Unable to read parents line: %s", err)))
107
parents = ParseLenDelimitedLine(line)
121
109
line, err = r.ReadSlice('\n')
123
err = ParseError(fmt.Sprintf("Unable to read ghosts line: %s", err))
111
panic(ParseError(fmt.Sprintf("Unable to read ghosts line: %s", err)))
127
ghosts, err = ParseLenDelimitedLine(line)
114
ghosts = ParseLenDelimitedLine(line)
131
118
func readHeader(r *bufio.Reader) (state *State, info headerInfo, hr *HashReader, err os.Error) {
132
info, err = readPrelude(r)
119
info = readPrelude(r)
136
120
// hr = &HashReader{crc32.NewIEEE(), r}
137
parents, ghosts, err := readParentsGhosts(r)
121
parents, ghosts := readParentsGhosts(r)
138
122
state = new(State)
139
123
state.headerState = IN_MEMORY_UNMODIFIED
140
124
state.parents = parents
201
func (p *entryParser) getKey(key *entryKey) (err os.Error) {
184
func (p *entryParser) getKey(key *entryKey) {
202
185
// TODO: we probably want to share the dirname strings, so use some state
203
186
// to track that.
204
187
// TODO: is there a way to test that objects are the same object, rather
205
188
// than just equality?
206
189
var content []byte
207
if content, err = p.getNext(); err != nil {
208
err = ParseError(fmt.Sprintf("Unable to find dirname for key: %s", err))
190
content = p.getNext()
211
191
if bytes.Equal(content, p.curDirname) {
212
192
key.dirname = p.curDirnameS
215
195
p.curDirnameS = key.dirname
216
196
p.curDirname = []byte(key.dirname)
218
if content, err = p.getNext(); err != nil {
219
err = ParseError(fmt.Sprintf("Unable to find basename for key: %s", err))
222
key.basename = string(content)
223
if content, err = p.getNext(); err != nil {
224
err = ParseError(fmt.Sprintf("Unable to find fileID for key: %s", err))
227
key.fileID = string(content)
198
key.basename = string(p.getNext())
199
key.fileID = string(p.getNext())
231
func (p *entryParser) getDetails(d *details) (err os.Error) {
203
func (p *entryParser) getDetails(d *details) {
232
204
var content []byte
233
if content, err = p.getNext(); err != nil {
234
err = ParseError(fmt.Sprintf("minikind missing: %s", err))
205
content = p.getNext()
237
206
if len(content) != 1 {
238
err = ParseError("minikind must be a single byte")
207
panic(ParseError("minikind must be a single byte"))
241
209
d.minikind = content[0]
242
if content, err = p.getNext(); err != nil {
243
err = ParseError(fmt.Sprintf("fingerprint missing: %s", err))
246
d.fingerprint = string(content)
247
if content, err = p.getNext(); err != nil {
248
err = ParseError(fmt.Sprintf("size missing: %s", err))
210
d.fingerprint = string(p.getNext())
211
content = p.getNext()
251
213
if d.size, err = strconv.Atoui64(string(content)); err != nil {
252
err = ParseError(fmt.Sprintf("invalid size: %s", err))
255
if content, err = p.getNext(); err != nil {
256
err = ParseError(fmt.Sprintf("executable missing: %s", err))
214
panic(ParseError(fmt.Sprintf("invalid size: %s", err)))
216
content = p.getNext()
259
217
if len(content) != 1 {
260
err = ParseError("executable must be a single byte long")
218
panic(ParseError("executable must be a single byte long"))
263
220
switch content[0] {
267
224
d.executable = false
269
err = ParseError("executable must be either 'n' or 'y'")
226
panic(ParseError("executable must be either 'n' or 'y'"))
274
func (p *entryParser) getWorkingDetails(d *workingDetails) (err os.Error) {
276
if err = p.getDetails(&d.details); err != nil {
279
if content, err = p.getNext(); err != nil {
280
err = ParseError(fmt.Sprintf("packedHash missing: %s", err))
231
func (p *entryParser) getWorkingDetails(d *workingDetails) {
232
p.getDetails(&d.details)
283
233
// TODO: Consider doing a hex decode here to save packedHash as a 20-byte
284
234
// array, rather than a string
285
d.packedHash = string(content)
289
func (p *entryParser) getParentDetails(d *parentDetails) (err os.Error) {
291
if err = p.getDetails(&d.details); err != nil {
294
if content, err = p.getNext(); err != nil {
295
err = ParseError(fmt.Sprintf("revisionID missing: %s", err))
298
d.revisionID = string(content)
302
func (p *entryParser) getEntry(e *entry) (err os.Error) {
303
if err = p.getKey(&e.key); err != nil {
306
if err = p.getWorkingDetails(&e.work); err != nil {
235
d.packedHash = string(p.getNext())
239
func (p *entryParser) getParentDetails(d *parentDetails) {
240
p.getDetails(&d.details)
241
d.revisionID = string(p.getNext())
245
func (p *entryParser) getEntry(e *entry) {
247
p.getWorkingDetails(&e.work)
309
248
if p.numParents > 0 {
310
if err = p.getParentDetails(&e.basis); err != nil {
311
err = ParseError(fmt.Sprintf("failed to read basis: %s", err))
249
p.getParentDetails(&e.basis)
315
251
for i := 1; i < p.numParents; i++ {
316
252
parent := parentDetails{}
317
if err = p.getParentDetails(&parent); err != nil {
318
err = ParseError(fmt.Sprintf("failed to read parent %d: %s", i, err))
253
p.getParentDetails(&parent)
321
254
e.extra = append(e.extra, parent)
323
256
if len(p.text) < 1 || p.text[0] != '\n' {
324
err = ParseError("missing trailing \\n\\x00")
257
panic(ParseError("missing trailing \\n\\x00"))
330
func readEntries(state *State, r *bufio.Reader, numEntries uint) (err os.Error) {
331
p := newEntryParser(make([]byte, 0), len(state.parents) - len(state.ghosts))
262
func readEntries(state *State, r *bufio.Reader, numEntries uint) {
263
p := newEntryParser(make([]byte, 0), len(state.parents)-len(state.ghosts))
332
264
entries := make([]entry, numEntries)
333
266
for _, e := range entries {
334
267
// The content is actually delimited with '\n\0'
335
268
if p.text, err = r.ReadSlice('\n'); err != nil {
338
if err = p.getEntry(&e); err != nil {
341
272
// Consume the next '\0'
342
273
tail, err := r.ReadByte()
346
277
if tail != '\x00' {
347
err = ParseError("entry was missing final '\\x00'")
278
panic(ParseError("entry was missing final '\\x00'"))
350
281
state.entries = entries