diff --git a/lib/go/camli/schema/fileread_test.go b/lib/go/camli/schema/fileread_test.go index 5f6d9201f..efe5987ae 100644 --- a/lib/go/camli/schema/fileread_test.go +++ b/lib/go/camli/schema/fileread_test.go @@ -47,10 +47,37 @@ func part(blob *test.Blob, offset, size uint64) *ContentPart { return &ContentPart{BlobRef: blob.BlobRef(), Size: size, Offset: offset} } +// filePart returns a ContentPart that references a file JSON schema +// blob made of the provided content parts. +func filePart(cps []*ContentPart) *ContentPart { + m := NewCommonFilenameMap("") + fileSize := int64(0) + cpl := []ContentPart{} + for _, cp := range cps { + fileSize += int64(cp.Size) + cpl = append(cpl, *cp) + } + err := PopulateRegularFileMap(m, fileSize, cpl) + if err != nil { + panic(err.String()) + } + json, err := MapToCamliJson(m) + if err != nil { + panic(err.String()) + } + tb := &test.Blob{json} + testFetcher.AddBlob(tb) + return &ContentPart{SubBlobRef: tb.BlobRef(), Size: uint64(fileSize)} +} + func all(blob *test.Blob) *ContentPart { return part(blob, 0, uint64(blob.Size())) } +func zero(size uint64) *ContentPart { + return &ContentPart{Size: size} +} + func parts(parts ...*ContentPart) []*ContentPart { return parts } @@ -78,6 +105,7 @@ var readTests = []readTest{ {parts(all(blobA), all(blobB), all(blobC)), 20, "CCCCCccccc"}, {parts(all(blobA), all(blobB), all(blobC)), 22, "CCCccccc"}, {parts(part(blobA, 5, 5), part(blobB, 0, 5), part(blobC, 4, 2)), 1, "aaaaBBBBBCc"}, + {parts(all(blobA), zero(2), all(blobB)), 5, "aaaaa\x00\x00BBBBBbbbbb"}, } func TestReader(t *testing.T) { diff --git a/lib/go/camli/schema/filereader.go b/lib/go/camli/schema/filereader.go index 82c478947..80bd3e519 100644 --- a/lib/go/camli/schema/filereader.go +++ b/lib/go/camli/schema/filereader.go @@ -88,14 +88,18 @@ func (fr *FileReader) closeOpenBlobs() { } } -func (fr *FileReader) readerFor(br *blobref.BlobRef) (blobref.ReadSeekCloser, os.Error) { +func (fr *FileReader) readerFor(br *blobref.BlobRef) (rsc blobref.ReadSeekCloser, err os.Error) { if fr.crbr == br { return fr.cr, nil } fr.closeOpenBlobs() - rsc, _, ferr := fr.fetcher.Fetch(br) - if ferr != nil { - return nil, ferr + if br != nil { + rsc, _, err = fr.fetcher.Fetch(br) + if err != nil { + return + } + } else { + rsc = &zeroReader{} } fr.crbr = br fr.cr = rsc @@ -127,8 +131,13 @@ func (fr *FileReader) Read(p []byte) (n int, err os.Error) { } br := cp.blobref() - if br == nil { - return 0, fmt.Errorf("no blobref in content part %d", fr.ci) + sbr := cp.subblobref() + if br != nil && sbr != nil { + return 0, fmt.Errorf("content part index %d has both blobRef and subFileBlobRef", fr.ci) + } + if sbr != nil { + // TODO + return 0, fmt.Errorf("TODO: unsupported subFileBlobRef in content part index %d", fr.ci) } rsc, ferr := fr.readerFor(br) @@ -164,3 +173,22 @@ func minu64(a, b uint64) uint64 { } return b } + +type zeroReader struct {} + +func (*zeroReader) Read(p []byte) (int, os.Error) { + for i := range p { + p[i] = 0 + } + return len(p), nil +} + +func (*zeroReader) Close() os.Error { + return nil +} + +func (*zeroReader) Seek(offset int64, whence int) (newFilePos int64, err os.Error) { + // Caller is ignoring our newFilePos return value. + return 0, nil +} + diff --git a/lib/go/camli/schema/schema.go b/lib/go/camli/schema/schema.go index a2cb1217a..bdf94e600 100644 --- a/lib/go/camli/schema/schema.go +++ b/lib/go/camli/schema/schema.go @@ -99,12 +99,19 @@ type ContentPart struct { } func (cp *ContentPart) blobref() *blobref.BlobRef { - if cp.BlobRef == nil { + if cp.BlobRef == nil && cp.BlobRefString != "" { cp.BlobRef = blobref.Parse(cp.BlobRefString) } return cp.BlobRef } +func (cp *ContentPart) subblobref() *blobref.BlobRef { + if cp.SubBlobRef == nil && cp.SubBlobRefString != "" { + cp.SubBlobRef = blobref.Parse(cp.SubBlobRefString) + } + return cp.SubBlobRef +} + func stringFromMixedArray(parts []interface{}) string { buf := new(bytes.Buffer) for _, part := range parts {