PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/moul/x/plan9/synfs/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / x / plan9 / synfs
Files
4 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/x/plan9/synfs/v0
gno
0.9

Files (4)

  • README.mdmarkdown
  • gnomod.tomltoml
  • synfs.gnogno
  • synfs_test.gnogno
synfs.gnogno
1// Package synfs builds a synthetic, read-only ninep file server whose contents2// are computed by a function at read time.3//4// This is the package that makes the rest of the suite adoptable. Plan 9's5// device drivers are not storage, they are code behind a name: reading6// /dev/time runs a function. A realm that wants to publish its state as a7// browsable tree does the same thing here, in a few lines, and gets ls, cat,8// stat and mountability for free:9//10//	t := synfs.New("dev", "g1...", func() int64 { return runtime.ChainHeight() })11//	t.Root().12//		Add("sysname", func() string { return runtime.ChainID() }).13//		Add("height", func() string { return strconv.FormatInt(runtime.ChainHeight(), 10) })14//15// The tree is READ-ONLY by construction, which is what makes it safe to hand16// to another realm's namespace: every method is free of side effects, so it17// can be called from a frame that does not own these objects.18//19// A synthetic file reports Qid.Version 0 forever. Its contents can change on20// every block, so a version would be a lie; clients that need change detection21// should read the file.22//23// NOTICE. Plan 9 from Bell Labs is the work of the Computing Science Research24// Center at Bell Labs; the name and the marks are theirs, and the copyright is25// held by the Plan 9 Foundation (https://p9f.org). This package is not26// affiliated with, endorsed by, or sponsored by them, and contains no Plan 927// code: it borrows the vocabulary so that the design reads without a glossary,28// and it is an homage, asking what that ecosystem's spirit looks like on a29// chain. Full attribution: NOTICE.md at the root of moul/gno-contracts.30package synfs3132import (33	"gno.land/p/nt/avl/v0"3435	ninep "gno.land/p/moul/x/plan9/ninep/v0"36)3738// ReadFn computes a whole file.39type ReadFn func() string4041// RangeFn serves a 9P read window directly, for files that are cheaper (or42// only possible) to generate a slice at a time, such as an endless one.43type RangeFn func(off, count int64) (string, error)4445// Tree owns the qid allocator, the clock and the owner name shared by every46// node in one synthetic server.47type Tree struct {48	root  *Dir49	next  uint6450	clock func() int6451	uid   string52}5354// New returns a tree whose root directory is named name, owned by uid, using55// clock (normally the block height) as every node's mtime. clock may be nil,56// in which case the mtime is zero.57func New(name, uid string, clock func() int64) *Tree {58	t := &Tree{clock: clock, uid: uid}59	t.root = t.NewDir(name)60	return t61}6263// Root returns the tree's root directory.64func (t *Tree) Root() *Dir { return t.root }6566// Uid returns the owner stamped on every node.67func (t *Tree) Uid() string { return t.uid }6869func (t *Tree) now() int64 {70	if t.clock == nil {71		return 072	}73	return t.clock()74}7576func (t *Tree) qid(dir bool) ninep.Qid {77	t.next++78	qt := ninep.QTFILE79	if dir {80		qt = ninep.QTDIR81	}82	return ninep.Qid{Type: qt, Version: 0, Path: t.next}83}8485// NewDir returns a detached directory belonging to this tree. Attach it with86// AddDir.87func (t *Tree) NewDir(name string) *Dir {88	return &Dir{89		tree:     t,90		name:     name,91		perm:     ninep.DMDIR | 0555,92		qid:      t.qid(true),93		children: avl.NewTree(),94	}95}9697// Dir is a synthetic directory. Its children are held in an avl tree, so a98// listing is ordered by name and identical on every node.99type Dir struct {100	tree     *Tree101	name     string102	perm     ninep.Perm103	qid      ninep.Qid104	children *avl.Tree // name -> ninep.File105}106107// Add attaches a read-only file computed by fn, mode 0444. It returns the108// directory, so calls chain.109func (d *Dir) Add(name string, fn ReadFn) *Dir {110	return d.AddPerm(name, 0444, fn)111}112113// AddPerm is Add with an explicit mode.114func (d *Dir) AddPerm(name string, perm ninep.Perm, fn ReadFn) *Dir {115	whole := fn116	return d.AddRange(name, perm, func(off, count int64) (string, error) {117		return ninep.Slice(whole(), off, count), nil118	})119}120121// AddRange attaches a file that serves a read window itself. Use it for a file122// with no natural end, where materialising the whole thing would be wrong.123func (d *Dir) AddRange(name string, perm ninep.Perm, fn RangeFn) *Dir {124	if !ninep.ValidName(name) {125		panic("synfs: invalid file name: " + name)126	}127	d.children.Set(name, &file{128		tree: d.tree,129		name: name,130		perm: perm &^ ninep.DMDIR,131		qid:  d.tree.qid(false),132		fn:   fn,133	})134	return d135}136137// AddDir attaches a subdirectory built with Tree.NewDir.138func (d *Dir) AddDir(sub *Dir) *Dir {139	if !ninep.ValidName(sub.name) {140		panic("synfs: invalid directory name: " + sub.name)141	}142	d.children.Set(sub.name, sub)143	return d144}145146// AddFile attaches any ninep.File under the given name, which is how a147// synthetic tree splices in a tree served by something else.148func (d *Dir) AddFile(name string, f ninep.File) *Dir {149	if !ninep.ValidName(name) {150		panic("synfs: invalid file name: " + name)151	}152	d.children.Set(name, f)153	return d154}155156// Names returns the directory's entries, in order. Useful for a file that157// wants to describe its own directory, as /dev/drivers does.158func (d *Dir) Names() []string {159	out := []string{}160	d.children.Iterate("", "", func(k string, _ any) bool {161		out = append(out, k)162		return false163	})164	return out165}166167// Stat implements ninep.File.168func (d *Dir) Stat() ninep.Stat {169	return ninep.Stat{170		Qid:   d.qid,171		Mode:  d.perm,172		Mtime: d.tree.now(),173		Name:  d.name,174		Uid:   d.tree.uid,175		Gid:   d.tree.uid,176		Muid:  d.tree.uid,177	}178}179180// Walk implements ninep.File.181func (d *Dir) Walk(name string) (ninep.File, error) {182	if !ninep.ValidName(name) {183		return nil, ninep.ErrBadName184	}185	v := d.children.Get(name)186	if v == nil {187		return nil, ninep.ErrNotExist188	}189	return v.(ninep.File), nil190}191192// Read implements ninep.File.193func (d *Dir) Read(off, count int64) (string, error) { return "", ninep.ErrIsDir }194195// ReadDir implements ninep.File.196func (d *Dir) ReadDir() ([]ninep.Stat, error) {197	out := []ninep.Stat{}198	d.children.Iterate("", "", func(_ string, v any) bool {199		out = append(out, v.(ninep.File).Stat())200		return false201	})202	return out, nil203}204205type file struct {206	tree *Tree207	name string208	perm ninep.Perm209	qid  ninep.Qid210	fn   RangeFn211}212213func (f *file) Stat() ninep.Stat {214	// A synthetic file has no stored length. 9P servers for such files report215	// zero rather than run the generator just to measure it, and so does this.216	return ninep.Stat{217		Qid:   f.qid,218		Mode:  f.perm,219		Mtime: f.tree.now(),220		Name:  f.name,221		Uid:   f.tree.uid,222		Gid:   f.tree.uid,223		Muid:  f.tree.uid,224	}225}226227func (f *file) Walk(name string) (ninep.File, error) { return nil, ninep.ErrNotDir }228229func (f *file) Read(off, count int64) (string, error) { return f.fn(off, count) }230231func (f *file) ReadDir() ([]ninep.Stat, error) { return nil, ninep.ErrNotDir }232

Functions

not supported for pure packages by the node (vm/qfuncs)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.