// NewReader returns a new table reader for the file. Closing the reader will // close the file. func NewReader(f db.File, o *db.Options) *Reader { r := &Reader{ file: f, comparer: o.GetComparer(), verifyChecksums: o.GetVerifyChecksums(), } if f == nil { r.err = errors.New("leveldb/table: nil file") return r } stat, err := f.Stat() if err != nil { r.err = fmt.Errorf("leveldb/table: invalid table (could not stat file): %v", err) return r } var footer [footerLen]byte if stat.Size() < int64(len(footer)) { r.err = errors.New("leveldb/table: invalid table (file size is too small)") return r } _, err = f.ReadAt(footer[:], stat.Size()-int64(len(footer))) if err != nil && err != io.EOF { r.err = fmt.Errorf("leveldb/table: invalid table (could not read footer): %v", err) return r } if string(footer[footerLen-len(magic):footerLen]) != magic { r.err = errors.New("leveldb/table: invalid table (bad magic number)") return r } // Ignore the metaindex. _, n := decodeBlockHandle(footer[:]) if n == 0 { r.err = errors.New("leveldb/table: invalid table (bad metaindex block handle)") return r } // Read the index into memory. indexBH, n := decodeBlockHandle(footer[n:]) if n == 0 { r.err = errors.New("leveldb/table: invalid table (bad index block handle)") return r } r.index, r.err = r.readBlock(indexBH) return r }
func TestBasics(t *testing.T) { fs := New() testCases := []string{ // Create a top-level file. "1a: create /foo", // Create a child of that file. It should fail, since /foo is not a directory. "2a: create /foo/x fails", // Create a third-level file. It should fail, since /bar has not been created. // Similarly, opening that file should fail. "3a: create /bar/baz/y fails", "3b: open /bar/baz/y fails", // Make the /bar/baz directory; create a third-level file. Creation should now succeed. "4a: mkdirall /bar/baz", "4b: f = create /bar/baz/y", "4c: f.stat.name == y", // Write some data; read it back. "5a: f.write abcde", "5b: f.close", "5c: f = open /bar/baz/y", "5d: f.read 5 == abcde", "5e: f.readat 2 1 == bc", "5f: f.close", // Remove the file twice. The first should succeed, the second should fail. "6a: remove /bar/baz/y", "6b: remove /bar/baz/y fails", "6c: open /bar/baz/y fails", // Rename /foo to /goo. Trying to open /foo should succeed before the rename and // fail afterwards, and vice versa for /goo. "7a: open /foo", "7b: open /goo fails", "7c: rename /foo /goo", "7d: open /foo fails", "7e: open /goo", // Create /bar/baz/z and rename /bar/baz to /bar/caz. "8a: create /bar/baz/z", "8b: open /bar/baz/z", "8c: open /bar/caz/z fails", "8d: rename /bar/baz /bar/caz", "8e: open /bar/baz/z fails", "8f: open /bar/caz/z", } var f db.File for _, tc := range testCases { s := strings.Split(tc, " ")[1:] saveF := s[0] == "f" && s[1] == "=" if saveF { s = s[2:] } fails := s[len(s)-1] == "fails" if fails { s = s[:len(s)-1] } var ( fi os.FileInfo g db.File err error ) switch s[0] { case "create": g, err = fs.Create(normalize(s[1])) case "open": g, err = fs.Open(normalize(s[1])) case "mkdirall": err = fs.MkdirAll(normalize(s[1]), 0755) case "remove": err = fs.Remove(normalize(s[1])) case "rename": err = fs.Rename(normalize(s[1]), normalize(s[2])) case "f.write": _, err = f.Write([]byte(s[1])) case "f.read": n, _ := strconv.Atoi(s[1]) buf := make([]byte, n) _, err = io.ReadFull(f, buf) if err != nil { break } if got, want := string(buf), s[3]; got != want { t.Fatalf("%q: got %q, want %q", tc, got, want) } case "f.readat": n, _ := strconv.Atoi(s[1]) off, _ := strconv.Atoi(s[2]) buf := make([]byte, n) _, err = f.ReadAt(buf, int64(off)) if err != nil { break } if got, want := string(buf), s[4]; got != want { t.Fatalf("%q: got %q, want %q", tc, got, want) } case "f.close": f, err = nil, f.Close() case "f.stat.name": fi, err = f.Stat() if err != nil { break } if got, want := fi.Name(), s[2]; got != want { t.Fatalf("%q: got %q, want %q", tc, got, want) } default: t.Fatalf("bad test case: %q", tc) } if saveF { f, g = g, nil } else if g != nil { g.Close() } if fails { if err == nil { t.Fatalf("%q: got nil error, want non-nil", tc) } } else { if err != nil { t.Fatalf("%q: %v", tc, err) } } } }