forked from keybase/client
/
block_types.go
726 lines (633 loc) · 20.1 KB
/
block_types.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.
package data
import (
"fmt"
"strconv"
"sync"
"github.com/keybase/client/go/kbfs/kbfshash"
"github.com/keybase/go-codec/codec"
)
// Int64Offset represents the offset of a block within a file.
type Int64Offset int64
var _ Offset = Int64Offset(0)
// Equals implements the Offset interface for Int64Offset.
func (i Int64Offset) Equals(other Offset) bool {
otherI, ok := other.(Int64Offset)
if !ok {
panic(fmt.Sprintf("Can't compare against non-int offset: %T", other))
}
return int64(i) == int64(otherI)
}
// Less implements the Offset interface for Int64Offset.
func (i Int64Offset) Less(other Offset) bool {
otherI, ok := other.(Int64Offset)
if !ok {
panic(fmt.Sprintf("Can't compare against non-int offset: %T", other))
}
return int64(i) < int64(otherI)
}
func (i Int64Offset) String() string {
return strconv.FormatInt(int64(i), 10)
}
// StringOffset represents the offset of a block within a directory.
type StringOffset string
var _ Offset = (*StringOffset)(nil)
// Equals implements the Offset interface for StringOffset.
func (s *StringOffset) Equals(other Offset) bool {
if s == nil {
return other == nil
} else if other == nil {
return false
}
otherS, ok := other.(*StringOffset)
if !ok {
panic(fmt.Sprintf("Can't compare against non-string offset: %T", other))
}
return string(*s) == string(*otherS)
}
// Less implements the Offset interface for StringOffset.
func (s *StringOffset) Less(other Offset) bool {
if s == nil {
return other != nil
} else if other == nil {
return false
}
otherS, ok := other.(*StringOffset)
if !ok {
panic(fmt.Sprintf("Can't compare against non-string offset: %T", other))
}
return string(*s) < string(*otherS)
}
func (s *StringOffset) String() string {
return string(*s)
}
// IndirectDirPtr pairs an indirect dir block with the start of that
// block's range of directory entries (inclusive)
type IndirectDirPtr struct {
// TODO: Make sure that the block is not dirty when the EncodedSize
// field is non-zero.
BlockInfo
Off StringOffset `codec:"o"`
codec.UnknownFieldSetHandler
}
// IndirectFilePtr pairs an indirect file block with the start of that
// block's range of bytes (inclusive)
//
// If `Holes` is true, then this pointer is part of a list of pointers
// that has non-continuous offsets; that is, the offset of ptr `i`
// plus the length of the corresponding block contents is less than
// the offset of ptr `i`+1.
type IndirectFilePtr struct {
// When the EncodedSize field is non-zero, the block must not
// be dirty.
BlockInfo
Off Int64Offset `codec:"o"`
// Marker for files with holes. This is here for historical
// reasons; a `FileBlock` should be treated as having a `HasHoles`
// flag set to true if any of its IPtrs have `Holes` set to true.
Holes bool `codec:"h,omitempty"`
codec.UnknownFieldSetHandler
}
// CommonBlock holds block data that is common for both subdirectories
// and files.
type CommonBlock struct {
// IsInd indicates where this block is so big it requires indirect pointers
IsInd bool `codec:"s"`
codec.UnknownFieldSetHandler
cacheMtx sync.RWMutex
// cachedEncodedSize is the locally-cached (non-serialized)
// encoded size for this block.
cachedEncodedSize uint32
}
var _ Block = (*CommonBlock)(nil)
// GetEncodedSize implements the Block interface for CommonBlock
func (cb *CommonBlock) GetEncodedSize() uint32 {
cb.cacheMtx.RLock()
defer cb.cacheMtx.RUnlock()
return cb.cachedEncodedSize
}
// SetEncodedSize implements the Block interface for CommonBlock
func (cb *CommonBlock) SetEncodedSize(size uint32) {
cb.cacheMtx.Lock()
defer cb.cacheMtx.Unlock()
cb.cachedEncodedSize = size
}
// DataVersion returns data version for this block.
func (cb *CommonBlock) DataVersion() Ver {
return FirstValidVer
}
// NewEmpty implements the Block interface for CommonBlock.
func (cb *CommonBlock) NewEmpty() Block {
return NewCommonBlock()
}
// NewEmptier implements the Block interface for CommonBlock.
func (cb *CommonBlock) NewEmptier() func() Block {
return NewCommonBlock
}
// ToCommonBlock implements the Block interface for CommonBlock.
func (cb *CommonBlock) ToCommonBlock() *CommonBlock {
return cb
}
// IsIndirect implements the Block interface for CommonBlock.
func (cb *CommonBlock) IsIndirect() bool {
return cb.IsInd
}
// IsTail implements the Block interface for CommonBlock.
func (cb *CommonBlock) IsTail() bool {
panic("CommonBlock doesn't know how to compute IsTail")
}
// OffsetExceedsData implements the Block interface for CommonBlock.
func (cb *CommonBlock) OffsetExceedsData(_, _ Offset) bool {
panic("CommonBlock doesn't implement data methods")
}
// Set implements the Block interface for CommonBlock.
func (cb *CommonBlock) Set(other Block) {
otherCommon := other.ToCommonBlock()
cb.IsInd = otherCommon.IsInd
cb.UnknownFieldSetHandler = otherCommon.UnknownFieldSetHandler
cb.SetEncodedSize(otherCommon.GetEncodedSize())
}
// BytesCanBeDirtied implements the Block interface for CommonBlock.
func (cb *CommonBlock) BytesCanBeDirtied() int64 {
return 0
}
// DeepCopy copies a CommonBlock without the lock.
func (cb *CommonBlock) DeepCopy() CommonBlock {
return CommonBlock{
IsInd: cb.IsInd,
// We don't need to copy UnknownFieldSetHandler because it's immutable.
UnknownFieldSetHandler: cb.UnknownFieldSetHandler,
cachedEncodedSize: cb.GetEncodedSize(),
}
}
// NewCommonBlock returns a generic block, unsuitable for caching.
func NewCommonBlock() Block {
return &CommonBlock{}
}
// NewCommonBlockForTesting returns a common block with some of the
// internal state set, which is useful for testing.
func NewCommonBlockForTesting(
isInd bool, cachedEncodedSize uint32) CommonBlock {
return CommonBlock{
IsInd: isInd,
cachedEncodedSize: cachedEncodedSize,
}
}
// DirBlock is the contents of a directory
type DirBlock struct {
CommonBlock
// if not indirect, a map of path name to directory entry
Children map[string]DirEntry `codec:"c,omitempty"`
// if indirect, contains the indirect pointers to the next level of blocks
IPtrs []IndirectDirPtr `codec:"i,omitempty"`
}
var _ BlockWithPtrs = (*DirBlock)(nil)
// NewDirBlock creates a new, empty DirBlock.
func NewDirBlock() Block {
return &DirBlock{
Children: make(map[string]DirEntry),
}
}
// NewDirBlockWithPtrs creates a new, empty DirBlock.
func NewDirBlockWithPtrs(isInd bool) BlockWithPtrs {
db := NewDirBlock().(*DirBlock)
db.IsInd = isInd
return db
}
// NewEmpty implements the Block interface for DirBlock
func (db *DirBlock) NewEmpty() Block {
return NewDirBlock()
}
// NewEmptier implements the Block interface for DirBlock.
func (db *DirBlock) NewEmptier() func() Block {
return NewDirBlock
}
// IsTail implements the Block interface for DirBlock.
func (db *DirBlock) IsTail() bool {
if db.IsInd {
return len(db.IPtrs) == 0
}
for _, de := range db.Children {
if de.Type != Sym {
return false
}
}
return true
}
// DataVersion returns data version for this block, which is assumed
// to have been modified locally.
func (db *DirBlock) DataVersion() Ver {
if db.IsInd {
return IndirectDirsVer
}
return FirstValidVer
}
// ToCommonBlock implements the Block interface for DirBlock.
func (db *DirBlock) ToCommonBlock() *CommonBlock {
return &db.CommonBlock
}
// Set implements the Block interface for DirBlock
func (db *DirBlock) Set(other Block) {
otherDb := other.(*DirBlock)
dbCopy := otherDb.DeepCopy()
db.Children = dbCopy.Children
db.IPtrs = dbCopy.IPtrs
db.ToCommonBlock().Set(dbCopy.ToCommonBlock())
}
// DeepCopy makes a complete copy of a DirBlock
func (db *DirBlock) DeepCopy() *DirBlock {
childrenCopy := make(map[string]DirEntry, len(db.Children))
for k, v := range db.Children {
childrenCopy[k] = v
}
var iptrsCopy []IndirectDirPtr
if db.IsInd {
iptrsCopy = make([]IndirectDirPtr, len(db.IPtrs))
copy(iptrsCopy, db.IPtrs)
}
return &DirBlock{
CommonBlock: db.CommonBlock.DeepCopy(),
Children: childrenCopy,
IPtrs: iptrsCopy,
}
}
// OffsetExceedsData implements the Block interface for DirBlock.
func (db *DirBlock) OffsetExceedsData(startOff, off Offset) bool {
// DirBlocks have open-ended children maps, so theoretically this
// block could have children all the way to the end of the
// alphabet.
return false
}
// BytesCanBeDirtied implements the Block interface for DirBlock.
func (db *DirBlock) BytesCanBeDirtied() int64 {
// Dir blocks don't track individual dirty bytes.
return 0
}
// FirstOffset implements the Block interface for DirBlock.
func (db *DirBlock) FirstOffset() Offset {
firstString := StringOffset("")
return &firstString
}
// NumIndirectPtrs implements the BlockWithPtrs interface for DirBlock.
func (db *DirBlock) NumIndirectPtrs() int {
if !db.IsInd {
panic("NumIndirectPtrs called on a direct directory block")
}
return len(db.IPtrs)
}
// IndirectPtr implements the BlockWithPtrs interface for DirBlock.
func (db *DirBlock) IndirectPtr(i int) (BlockInfo, Offset) {
if !db.IsInd {
panic("IndirectPtr called on a direct directory block")
}
iptr := db.IPtrs[i]
off := iptr.Off
return iptr.BlockInfo, &off
}
// AppendNewIndirectPtr implements the BlockWithPtrs interface for FileBlock.
func (db *DirBlock) AppendNewIndirectPtr(ptr BlockPointer, off Offset) {
if !db.IsInd {
panic("AppendNewIndirectPtr called on a direct directory block")
}
sOff, ok := off.(*StringOffset)
if !ok {
panic(fmt.Sprintf("AppendNewIndirectPtr called on a directory block "+
"with a %T offset", off))
}
db.IPtrs = append(db.IPtrs, IndirectDirPtr{
BlockInfo: BlockInfo{
BlockPointer: ptr,
EncodedSize: 0,
},
Off: *sOff,
})
}
// ClearIndirectPtrSize implements the BlockWithPtrs interface for DirBlock.
func (db *DirBlock) ClearIndirectPtrSize(i int) {
if !db.IsInd {
panic("ClearIndirectPtrSize called on a direct directory block")
}
db.IPtrs[i].EncodedSize = 0
}
// SetIndirectPtrType implements the BlockWithPtrs interface for DirBlock.
func (db *DirBlock) SetIndirectPtrType(i int, dt BlockDirectType) {
if !db.IsInd {
panic("SetIndirectPtrType called on a direct directory block")
}
db.IPtrs[i].DirectType = dt
}
// SwapIndirectPtrs implements the BlockWithPtrs interface for DirBlock.
func (db *DirBlock) SwapIndirectPtrs(i int, other BlockWithPtrs, otherI int) {
otherDB, ok := other.(*DirBlock)
if !ok {
panic(fmt.Sprintf(
"SwapIndirectPtrs cannot swap between block types: %T", other))
}
db.IPtrs[i], otherDB.IPtrs[otherI] = otherDB.IPtrs[otherI], db.IPtrs[i]
}
// SetIndirectPtrOff implements the BlockWithPtrs interface for DirBlock.
func (db *DirBlock) SetIndirectPtrOff(i int, off Offset) {
if !db.IsInd {
panic("SetIndirectPtrOff called on a direct directory block")
}
sOff, ok := off.(*StringOffset)
if !ok {
panic(fmt.Sprintf("SetIndirectPtrOff called on a dirctory block "+
"with a %T offset", off))
}
db.IPtrs[i].Off = *sOff
}
// SetIndirectPtrInfo implements the BlockWithPtrs interface for DirBlock.
func (db *DirBlock) SetIndirectPtrInfo(i int, info BlockInfo) {
if !db.IsInd {
panic("SetIndirectPtrInfo called on a direct directory block")
}
db.IPtrs[i].BlockInfo = info
}
// TotalPlainSizeEstimate returns an estimate of the plaintext size of
// this directory block.
func (db *DirBlock) TotalPlainSizeEstimate(
plainSize int, bsplit BlockSplitter) int {
if !db.IsIndirect() || len(db.IPtrs) == 0 {
return plainSize
}
// If the top block is indirect, it's too costly to estimate the
// sizes by checking the plain sizes of all the leafs. Instead
// use the following imperfect heuristics:
//
// * If there are N child pointers, and the first one is a direct
// pointer, assume N-1 of them are full.
//
// * If there are N child pointers, and the first one is an
// indirect pointer, just give up and max out at the maximum
// number of indirect pointers in a block, assuming that at
// least one indirect block is full of pointers when there are
// at least 2 indirect levels in the tree.
//
// This isn't great since it overestimates in many cases
// (especially when removing entries), and can vastly unerestimate
// if there are more than 2 levels of indirection. But it seems
// unlikely that directory byte size matters for anything in real
// life. Famous last words, of course...
if db.IPtrs[0].DirectType == DirectBlock {
return MaxBlockSizeBytesDefault * (len(db.IPtrs) - 1)
}
return MaxBlockSizeBytesDefault * bsplit.MaxPtrsPerBlock()
}
// FileBlock is the contents of a file
type FileBlock struct {
CommonBlock
// if not indirect, the full contents of this block
Contents []byte `codec:"c,omitempty"`
// if indirect, contains the indirect pointers to the next level of blocks
IPtrs []IndirectFilePtr `codec:"i,omitempty"`
// this is used for caching plaintext (block.Contents) hash. It is used by
// only direct blocks.
hash *kbfshash.RawDefaultHash
}
var _ BlockWithPtrs = (*FileBlock)(nil)
// NewFileBlock creates a new, empty FileBlock.
func NewFileBlock() Block {
return &FileBlock{
Contents: make([]byte, 0),
}
}
// NewFileBlockWithPtrs creates a new, empty FileBlock.
func NewFileBlockWithPtrs(isInd bool) BlockWithPtrs {
fb := NewFileBlock().(*FileBlock)
fb.IsInd = isInd
return fb
}
// NewEmpty implements the Block interface for FileBlock
func (fb *FileBlock) NewEmpty() Block {
return &FileBlock{}
}
// NewEmptier implements the Block interface for FileBlock.
func (fb *FileBlock) NewEmptier() func() Block {
return NewFileBlock
}
// IsTail implements the Block interface for FileBlock.
func (fb *FileBlock) IsTail() bool {
if fb.IsInd {
return len(fb.IPtrs) == 0
}
return true
}
// DataVersion returns data version for this block, which is assumed
// to have been modified locally.
func (fb *FileBlock) DataVersion() Ver {
if !fb.IsInd {
return FirstValidVer
}
if len(fb.IPtrs) == 0 {
// This is a truncated file block that hasn't had its level of
// indirection removed.
return FirstValidVer
}
// If this is an indirect block, and none of its children are
// marked as direct blocks, then this must be a big file. Note
// that we do it this way, rather than returning on the first
// non-direct block, to support appending to existing files and
// making them big.
hasHoles := false
hasDirect := false
maxDirectType := UnknownDirectType
for i := range fb.IPtrs {
if maxDirectType != UnknownDirectType &&
fb.IPtrs[i].DirectType != UnknownDirectType &&
maxDirectType != fb.IPtrs[i].DirectType {
panic("Mixed data versions among indirect pointers")
}
if fb.IPtrs[i].DirectType > maxDirectType {
maxDirectType = fb.IPtrs[i].DirectType
}
if fb.IPtrs[i].DirectType == DirectBlock {
hasDirect = true
} else if fb.IPtrs[i].Holes {
hasHoles = true
}
// We can only safely break if both vars are definitely set to
// their final value.
if hasDirect && hasHoles {
break
}
}
if maxDirectType == UnknownDirectType {
panic("No known type for any indirect pointer")
}
if !hasDirect {
return AtLeastTwoLevelsOfChildrenVer
} else if hasHoles {
return ChildHolesVer
}
return FirstValidVer
}
// ToCommonBlock implements the Block interface for FileBlock.
func (fb *FileBlock) ToCommonBlock() *CommonBlock {
return &fb.CommonBlock
}
// Set implements the Block interface for FileBlock
func (fb *FileBlock) Set(other Block) {
otherFb := other.(*FileBlock)
fbCopy := otherFb.DeepCopy()
fb.Contents = fbCopy.Contents
fb.IPtrs = fbCopy.IPtrs
fb.ToCommonBlock().Set(fbCopy.ToCommonBlock())
// Ensure that the Set is complete from Go's perspective by calculating the
// hash on the new FileBlock if the old one has been set. This is mainly so
// tests can blindly compare that blocks are equivalent.
h := func() *kbfshash.RawDefaultHash {
otherFb.cacheMtx.RLock()
defer otherFb.cacheMtx.RUnlock()
return otherFb.hash
}()
if h != nil {
_ = fb.GetHash()
}
}
// DeepCopy makes a complete copy of a FileBlock
func (fb *FileBlock) DeepCopy() *FileBlock {
var contentsCopy []byte
if fb.Contents != nil {
contentsCopy = make([]byte, len(fb.Contents))
copy(contentsCopy, fb.Contents)
}
var iptrsCopy []IndirectFilePtr
if fb.IPtrs != nil {
iptrsCopy = make([]IndirectFilePtr, len(fb.IPtrs))
copy(iptrsCopy, fb.IPtrs)
}
return &FileBlock{
CommonBlock: fb.CommonBlock.DeepCopy(),
Contents: contentsCopy,
IPtrs: iptrsCopy,
}
}
// GetHash returns the hash of this FileBlock. If the hash is nil, it first
// calculates it.
func (fb *FileBlock) GetHash() kbfshash.RawDefaultHash {
h := func() *kbfshash.RawDefaultHash {
fb.cacheMtx.RLock()
defer fb.cacheMtx.RUnlock()
return fb.hash
}()
if h != nil {
return *h
}
_, hash := kbfshash.DoRawDefaultHash(fb.Contents)
fb.cacheMtx.Lock()
defer fb.cacheMtx.Unlock()
fb.hash = &hash
return *fb.hash
}
// OffsetExceedsData implements the Block interface for FileBlock.
func (fb *FileBlock) OffsetExceedsData(startOff, off Offset) bool {
if fb.IsInd {
panic("OffsetExceedsData called on an indirect file block")
}
if len(fb.Contents) == 0 {
return false
}
offI, ok := off.(Int64Offset)
if !ok {
panic(fmt.Sprintf("Bad offset of type %T passed to FileBlock", off))
}
startOffI, ok := startOff.(Int64Offset)
if !ok {
panic(fmt.Sprintf("Bad offset of type %T passed to FileBlock",
startOff))
}
return int64(offI) >= int64(startOffI)+int64(len(fb.Contents))
}
// BytesCanBeDirtied implements the Block interface for FileBlock.
func (fb *FileBlock) BytesCanBeDirtied() int64 {
return int64(len(fb.Contents))
}
// FirstOffset implements the Block interface for FileBlock.
func (fb *FileBlock) FirstOffset() Offset {
return Int64Offset(0)
}
// NumIndirectPtrs implements the BlockWithPtrs interface for FileBlock.
func (fb *FileBlock) NumIndirectPtrs() int {
if !fb.IsInd {
panic("NumIndirectPtrs called on a direct file block")
}
return len(fb.IPtrs)
}
// IndirectPtr implements the BlockWithPtrs interface for FileBlock.
func (fb *FileBlock) IndirectPtr(i int) (BlockInfo, Offset) {
if !fb.IsInd {
panic("IndirectPtr called on a direct file block")
}
iptr := fb.IPtrs[i]
return iptr.BlockInfo, iptr.Off
}
// AppendNewIndirectPtr implements the BlockWithPtrs interface for FileBlock.
func (fb *FileBlock) AppendNewIndirectPtr(ptr BlockPointer, off Offset) {
if !fb.IsInd {
panic("AppendNewIndirectPtr called on a direct file block")
}
iOff, ok := off.(Int64Offset)
if !ok {
panic(fmt.Sprintf("AppendNewIndirectPtr called on a file block "+
"with a %T offset", off))
}
fb.IPtrs = append(fb.IPtrs, IndirectFilePtr{
BlockInfo: BlockInfo{
BlockPointer: ptr,
EncodedSize: 0,
},
Off: iOff,
})
}
// ClearIndirectPtrSize implements the BlockWithPtrs interface for FileBlock.
func (fb *FileBlock) ClearIndirectPtrSize(i int) {
if !fb.IsInd {
panic("ClearIndirectPtrSize called on a direct file block")
}
fb.IPtrs[i].EncodedSize = 0
}
// SetIndirectPtrType implements the BlockWithPtrs interface for FileBlock.
func (fb *FileBlock) SetIndirectPtrType(i int, dt BlockDirectType) {
if !fb.IsInd {
panic("SetIndirectPtrType called on a direct file block")
}
fb.IPtrs[i].DirectType = dt
}
// SwapIndirectPtrs implements the BlockWithPtrs interface for FileBlock.
func (fb *FileBlock) SwapIndirectPtrs(i int, other BlockWithPtrs, otherI int) {
otherFB, ok := other.(*FileBlock)
if !ok {
panic(fmt.Sprintf(
"SwapIndirectPtrs cannot swap between block types: %T", other))
}
fb.IPtrs[i], otherFB.IPtrs[otherI] = otherFB.IPtrs[otherI], fb.IPtrs[i]
}
// SetIndirectPtrOff implements the BlockWithPtrs interface for FileBlock.
func (fb *FileBlock) SetIndirectPtrOff(i int, off Offset) {
if !fb.IsInd {
panic("SetIndirectPtrOff called on a direct file block")
}
iOff, ok := off.(Int64Offset)
if !ok {
panic(fmt.Sprintf("SetIndirectPtrOff called on a file block "+
"with a %T offset", off))
}
fb.IPtrs[i].Off = iOff
}
// SetIndirectPtrInfo implements the BlockWithPtrs interface for FileBlock.
func (fb *FileBlock) SetIndirectPtrInfo(i int, info BlockInfo) {
if !fb.IsInd {
panic("SetIndirectPtrInfo called on a direct file block")
}
fb.IPtrs[i].BlockInfo = info
}
// DefaultNewBlockDataVersion returns the default data version for new blocks.
func DefaultNewBlockDataVersion(holes bool) Ver {
if holes {
return ChildHolesVer
}
return FirstValidVer
}