bolt-piece.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. //go:build !noboltdb && !wasm
  2. // +build !noboltdb,!wasm
  3. package storage
  4. import (
  5. "encoding/binary"
  6. "io"
  7. "go.etcd.io/bbolt"
  8. "github.com/anacrolix/torrent/metainfo"
  9. )
  10. type boltPiece struct {
  11. db *bbolt.DB
  12. p metainfo.Piece
  13. ih metainfo.Hash
  14. key [24]byte
  15. }
  16. var (
  17. _ PieceImpl = (*boltPiece)(nil)
  18. dataBucketKey = []byte("data")
  19. )
  20. func (me *boltPiece) pc() PieceCompletionGetSetter {
  21. return boltPieceCompletion{me.db}
  22. }
  23. func (me *boltPiece) pk() metainfo.PieceKey {
  24. return metainfo.PieceKey{me.ih, me.p.Index()}
  25. }
  26. func (me *boltPiece) Completion() Completion {
  27. c, err := me.pc().Get(me.pk())
  28. switch err {
  29. case bbolt.ErrDatabaseNotOpen:
  30. return Completion{}
  31. case nil:
  32. default:
  33. panic(err)
  34. }
  35. return c
  36. }
  37. func (me *boltPiece) MarkComplete() error {
  38. return me.pc().Set(me.pk(), true)
  39. }
  40. func (me *boltPiece) MarkNotComplete() error {
  41. return me.pc().Set(me.pk(), false)
  42. }
  43. func (me *boltPiece) ReadAt(b []byte, off int64) (n int, err error) {
  44. err = me.db.View(func(tx *bbolt.Tx) error {
  45. db := tx.Bucket(dataBucketKey)
  46. if db == nil {
  47. return io.EOF
  48. }
  49. ci := off / chunkSize
  50. off %= chunkSize
  51. for len(b) != 0 {
  52. ck := me.chunkKey(int(ci))
  53. _b := db.Get(ck[:])
  54. // If the chunk is the wrong size, assume it's missing as we can't rely on the data.
  55. if len(_b) != chunkSize {
  56. return io.EOF
  57. }
  58. n1 := copy(b, _b[off:])
  59. off = 0
  60. ci++
  61. b = b[n1:]
  62. n += n1
  63. }
  64. return nil
  65. })
  66. return
  67. }
  68. func (me *boltPiece) chunkKey(index int) (ret [26]byte) {
  69. copy(ret[:], me.key[:])
  70. binary.BigEndian.PutUint16(ret[24:], uint16(index))
  71. return
  72. }
  73. func (me *boltPiece) WriteAt(b []byte, off int64) (n int, err error) {
  74. err = me.db.Update(func(tx *bbolt.Tx) error {
  75. db, err := tx.CreateBucketIfNotExists(dataBucketKey)
  76. if err != nil {
  77. return err
  78. }
  79. ci := off / chunkSize
  80. off %= chunkSize
  81. for len(b) != 0 {
  82. _b := make([]byte, chunkSize)
  83. ck := me.chunkKey(int(ci))
  84. copy(_b, db.Get(ck[:]))
  85. n1 := copy(_b[off:], b)
  86. db.Put(ck[:], _b)
  87. if n1 > len(b) {
  88. break
  89. }
  90. b = b[n1:]
  91. off = 0
  92. ci++
  93. n += n1
  94. }
  95. return nil
  96. })
  97. return
  98. }