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  }