| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- package core
- import (
- "errors"
- "io"
- "git.ycl.pub/yl32940/TsDB2/DB/core/page"
- )
- // blobChainReader 把"遍历 blob 链 (PageHdr.NextPage)"包装成 io.Reader.
- //
- // 与 blobChainWriter 一一对应:
- // - 一页 = 一个 blob chunk, 整页 Data 区是 raw 字节;
- // - 链由 PageHdr.NextPage 串, Read 读到 NextPage = 0 时返回 io.EOF.
- //
- // 调用方拿到 *blobChainReader 后可直接包成 snappy.Reader 或 io.ReadAll 等.
- type blobChainReader struct {
- db *DB
- cur uint32 // 当前页 PageID (0 表示已走完链)
- off uint16 // 当前页 Data 区已读偏移
- pageBuf []byte // 当前页缓存 (复用, 避免每页 make)
- done bool // 已读到 EOF
- }
- // newBlobChainReader 从首页开始构造 reader.
- func newBlobChainReader(db *DB, firstPageID uint32) (*blobChainReader, error) {
- if firstPageID == 0 {
- return &blobChainReader{db: db, done: true}, nil
- }
- r := &blobChainReader{
- db: db,
- cur: firstPageID,
- off: 0,
- pageBuf: make([]byte, PageSize),
- }
- if err := db.file.ReadPage(firstPageID, r.pageBuf); err != nil {
- return nil, err
- }
- return r, nil
- }
- // Read 实现 io.Reader. 一次 Read 可能跨页 (内部按 PageHdr.NextPage 衔接).
- //
- // 返回 (n, nil) 表示读了 n 字节但未到 EOF, (n, io.EOF) 表示链读完且 n>0,
- // (0, io.EOF) 表示调用前已在链尾. 错误一律返回 err (含 io.ErrUnexpectedEOF).
- func (r *blobChainReader) Read(p []byte) (int, error) {
- if r.done {
- return 0, io.EOF
- }
- const pageData = uint32(PageSize - page.PageHdrSize)
- total := 0
- for total < len(p) {
- used := page.HdrOf(r.pageBuf).UseSize - page.PageHdrSize
- // 注意用 int 计算: 调用方 (如 snappy 内部 io.ReadFull) 的 p 可达 64KB+,
- // 若用 uint16(len(p)-total) 会在 >65535 时截断为 0, 导致跳页丢数据.
- avail := int(used) - int(r.off)
- if avail > 0 {
- n := len(p) - total
- if n > avail {
- n = avail
- }
- copy(p[total:total+n],
- r.pageBuf[int(page.PageHdrSize)+int(r.off):int(page.PageHdrSize)+int(r.off)+n])
- r.off += uint16(n)
- total += n
- if total == len(p) {
- return total, nil
- }
- }
- // 当前页已读完, 跳下一页
- next := page.HdrOf(r.pageBuf).NextPage
- if next == 0 {
- r.done = true
- r.cur = 0
- if total > 0 {
- return total, io.EOF
- }
- return 0, io.EOF
- }
- if err := r.db.file.ReadPage(next, r.pageBuf); err != nil {
- return total, err
- }
- r.cur = next
- r.off = 0
- }
- return total, nil
- }
- // ErrBlobCorrupted blob 解压后字节数与 BlobRef.TotalLen 不一致 ——
- //
- // 意味着落盘链被外部破坏 (页被复用、链指针写错、压缩标志被翻等),
- // 不应继续把可疑字节返回给调用方.
- var ErrBlobCorrupted = errors.New("blob corrupted: decoded length mismatches TotalLen")
|