blob_reader.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. package core
  2. import (
  3. "errors"
  4. "io"
  5. "git.ycl.pub/yl32940/TsDB2/DB/core/page"
  6. )
  7. // blobChainReader 把"遍历 blob 链 (PageHdr.NextPage)"包装成 io.Reader.
  8. //
  9. // 与 blobChainWriter 一一对应:
  10. // - 一页 = 一个 blob chunk, 整页 Data 区是 raw 字节;
  11. // - 链由 PageHdr.NextPage 串, Read 读到 NextPage = 0 时返回 io.EOF.
  12. //
  13. // 调用方拿到 *blobChainReader 后可直接包成 snappy.Reader 或 io.ReadAll 等.
  14. type blobChainReader struct {
  15. db *DB
  16. cur uint32 // 当前页 PageID (0 表示已走完链)
  17. off uint16 // 当前页 Data 区已读偏移
  18. pageBuf []byte // 当前页缓存 (复用, 避免每页 make)
  19. done bool // 已读到 EOF
  20. }
  21. // newBlobChainReader 从首页开始构造 reader.
  22. func newBlobChainReader(db *DB, firstPageID uint32) (*blobChainReader, error) {
  23. if firstPageID == 0 {
  24. return &blobChainReader{db: db, done: true}, nil
  25. }
  26. r := &blobChainReader{
  27. db: db,
  28. cur: firstPageID,
  29. off: 0,
  30. pageBuf: make([]byte, PageSize),
  31. }
  32. if err := db.file.ReadPage(firstPageID, r.pageBuf); err != nil {
  33. return nil, err
  34. }
  35. return r, nil
  36. }
  37. // Read 实现 io.Reader. 一次 Read 可能跨页 (内部按 PageHdr.NextPage 衔接).
  38. //
  39. // 返回 (n, nil) 表示读了 n 字节但未到 EOF, (n, io.EOF) 表示链读完且 n>0,
  40. // (0, io.EOF) 表示调用前已在链尾. 错误一律返回 err (含 io.ErrUnexpectedEOF).
  41. func (r *blobChainReader) Read(p []byte) (int, error) {
  42. if r.done {
  43. return 0, io.EOF
  44. }
  45. const pageData = uint32(PageSize - page.PageHdrSize)
  46. total := 0
  47. for total < len(p) {
  48. used := page.HdrOf(r.pageBuf).UseSize - page.PageHdrSize
  49. // 注意用 int 计算: 调用方 (如 snappy 内部 io.ReadFull) 的 p 可达 64KB+,
  50. // 若用 uint16(len(p)-total) 会在 >65535 时截断为 0, 导致跳页丢数据.
  51. avail := int(used) - int(r.off)
  52. if avail > 0 {
  53. n := len(p) - total
  54. if n > avail {
  55. n = avail
  56. }
  57. copy(p[total:total+n],
  58. r.pageBuf[int(page.PageHdrSize)+int(r.off):int(page.PageHdrSize)+int(r.off)+n])
  59. r.off += uint16(n)
  60. total += n
  61. if total == len(p) {
  62. return total, nil
  63. }
  64. }
  65. // 当前页已读完, 跳下一页
  66. next := page.HdrOf(r.pageBuf).NextPage
  67. if next == 0 {
  68. r.done = true
  69. r.cur = 0
  70. if total > 0 {
  71. return total, io.EOF
  72. }
  73. return 0, io.EOF
  74. }
  75. if err := r.db.file.ReadPage(next, r.pageBuf); err != nil {
  76. return total, err
  77. }
  78. r.cur = next
  79. r.off = 0
  80. }
  81. return total, nil
  82. }
  83. // ErrBlobCorrupted blob 解压后字节数与 BlobRef.TotalLen 不一致 ——
  84. //
  85. // 意味着落盘链被外部破坏 (页被复用、链指针写错、压缩标志被翻等),
  86. // 不应继续把可疑字节返回给调用方.
  87. var ErrBlobCorrupted = errors.New("blob corrupted: decoded length mismatches TotalLen")