github.com/creachadair/ffs@v0.17.3/file/views.go (about) 1 // Copyright 2019 Michael J. Fromberger. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package file 16 17 import ( 18 "encoding/binary" 19 "io" 20 "slices" 21 "sort" 22 23 "golang.org/x/crypto/blake2b" 24 ) 25 26 // Child provides access to the children of a file. 27 type Child struct{ f *File } 28 29 // Has reports whether the file has a child with the given name. 30 func (c Child) Has(name string) bool { 31 c.f.mu.RLock() 32 defer c.f.mu.RUnlock() 33 _, ok := c.f.findChildLocked(name) 34 return ok 35 } 36 37 // Set makes kid a child of f under the given name. Set will panic if kid == nil. 38 func (c Child) Set(name string, kid *File) { 39 if kid == nil { 40 panic("set: nil file") 41 } 42 c.f.mu.Lock() 43 defer c.f.mu.Unlock() 44 kid.setName(name) 45 c.f.modifyLocked() 46 if i, ok := c.f.findChildLocked(name); ok { 47 c.f.kids[i].File = kid // replace an existing child 48 return 49 } 50 c.f.kids = append(c.f.kids, child{Name: name, File: kid}) 51 52 // Restore lexicographic order. 53 for i := len(c.f.kids) - 1; i > 0; i-- { 54 if c.f.kids[i].Name >= c.f.kids[i-1].Name { 55 break 56 } 57 c.f.kids[i], c.f.kids[i-1] = c.f.kids[i-1], c.f.kids[i] 58 } 59 } 60 61 // Len returns the number of children of the file. 62 func (c Child) Len() int { c.f.mu.RLock(); defer c.f.mu.RUnlock(); return len(c.f.kids) } 63 64 // Remove removes name as a child of f, and reports whether a change was made. 65 func (c Child) Remove(name string) bool { 66 c.f.mu.Lock() 67 defer c.f.mu.Unlock() 68 if i, ok := c.f.findChildLocked(name); ok { 69 c.f.modifyLocked() 70 c.f.kids = append(c.f.kids[:i], c.f.kids[i+1:]...) 71 return true 72 } 73 return false 74 } 75 76 // Names returns a lexicographically ordered slice of the names of all the 77 // children of the file. 78 func (c Child) Names() []string { 79 c.f.mu.RLock() 80 defer c.f.mu.RUnlock() 81 out := make([]string, len(c.f.kids)) 82 for i, kid := range c.f.kids { 83 out[i] = kid.Name 84 } 85 return out 86 } 87 88 // Release discards all up-to-date cached children of the file. It returns the 89 // number of records that were released. 90 func (c Child) Release() int { 91 c.f.mu.Lock() 92 defer c.f.mu.Unlock() 93 var n int 94 for i, kid := range c.f.kids { 95 if kid.File != nil && kid.Key != "" && kid.Key == kid.File.Key() { 96 c.f.kids[i].File = nil 97 n++ 98 } 99 } 100 return n 101 } 102 103 // Data is a view of the data associated with a file. 104 type Data struct{ f *File } 105 106 // Size returns the effective size of the file content in bytes. 107 func (d Data) Size() int64 { d.f.mu.RLock(); defer d.f.mu.RUnlock(); return d.f.data.totalBytes } 108 109 // Len returns the number of data blocks for the file. 110 func (d Data) Len() int { d.f.mu.RLock(); defer d.f.mu.RUnlock(); return d.lenLocked() } 111 112 func (d Data) lenLocked() int { 113 var nb int 114 for _, e := range d.f.data.extents { 115 nb += len(e.blocks) 116 } 117 return nb 118 } 119 120 // Keys returns the storage keys of the data blocks for the file. If the file 121 // has no binary data, the slice is empty. 122 func (d Data) Keys() []string { 123 d.f.mu.RLock() 124 defer d.f.mu.RUnlock() 125 nb := d.lenLocked() 126 if nb == 0 { 127 return nil 128 } 129 keys := make([]string, 0, nb) 130 for _, e := range d.f.data.extents { 131 for _, blk := range e.blocks { 132 keys = append(keys, blk.key) 133 } 134 } 135 slices.Sort(keys) 136 return slices.Compact(keys) 137 } 138 139 // Hash returns a cryptographic digest of binary content of the file. 140 // The digest is computed over the storage keys of the data blocks, in the 141 // exact order of their occurrence in the file index. 142 // 143 // The resulting digest is a valid fingerprint of the stored data, but is not 144 // equal to a direct hash of the raw data. 145 func (d Data) Hash() []byte { 146 h, _ := blake2b.New256(nil) // error condition is unreachable here 147 148 d.f.mu.RLock() 149 defer d.f.mu.RUnlock() 150 for _, e := range d.f.data.extents { 151 var buf [8]byte 152 153 // Mix in the base and size of each extent so that zero ranges are covered. 154 h.Write(binary.BigEndian.AppendUint64(buf[:], uint64(e.base))) 155 h.Write(binary.BigEndian.AppendUint64(buf[:], uint64(e.bytes))) 156 157 // Within each extent, mix in the block storage keys. 158 for _, blk := range e.blocks { 159 io.WriteString(h, blk.key) 160 } 161 } 162 return h.Sum(make([]byte, 0, h.Size())) 163 } 164 165 // XAttr provides access to the extended attributes of a file. 166 type XAttr struct{ f *File } 167 168 // Has reports whether the specified attribute is defined. 169 func (x XAttr) Has(key string) bool { 170 x.f.mu.RLock() 171 defer x.f.mu.RUnlock() 172 _, ok := x.f.xattr[key] 173 return ok 174 } 175 176 // Get returns the value corresponding to the given key, or "" if the key is 177 // not defined. 178 func (x XAttr) Get(key string) string { x.f.mu.RLock(); defer x.f.mu.RUnlock(); return x.f.xattr[key] } 179 180 // Set sets the specified xattr. 181 func (x XAttr) Set(key, value string) { 182 x.f.mu.Lock() 183 defer x.f.mu.Unlock() 184 defer x.f.invalLocked() 185 x.f.xattr[key] = value 186 } 187 188 // Len reports the number of extended attributes defined on f. 189 func (x XAttr) Len() int { x.f.mu.RLock(); defer x.f.mu.RUnlock(); return len(x.f.xattr) } 190 191 // Remove removes the specified xattr. 192 func (x XAttr) Remove(key string) { 193 x.f.mu.Lock() 194 defer x.f.mu.Unlock() 195 if _, ok := x.f.xattr[key]; ok { 196 delete(x.f.xattr, key) 197 x.f.invalLocked() 198 } 199 } 200 201 // Names returns a slice of the names of all the extended attributes defined. 202 func (x XAttr) Names() []string { 203 x.f.mu.RLock() 204 defer x.f.mu.RUnlock() 205 names := make([]string, 0, len(x.f.xattr)) 206 for key := range x.f.xattr { 207 names = append(names, key) 208 } 209 sort.Strings(names) 210 return names 211 } 212 213 // Clear removes all the extended attributes set on the file. 214 func (x XAttr) Clear() { 215 x.f.mu.Lock() 216 defer x.f.mu.Unlock() 217 if len(x.f.xattr) != 0 { 218 defer x.f.invalLocked() 219 clear(x.f.xattr) 220 } 221 }