~emersion/drmdb

8f5adfec99640fae00c75d8fe66bb91b1495166d — Simon Ser 3 months ago e081ab2
Introduce database.DB

Will be useful to cache stuff.
4 files changed, 168 insertions(+), 14 deletions(-)

M database/db.go
A database/fs.go
M server.go
M walk.go
M database/db.go => database/db.go +27 -0
@@ 1,1 1,28 @@
package database

import (
	"git.sr.ht/~emersion/drmdb/drmtree"
)

type DB struct {
}

func Open() (*DB, error) {
	return &DB{}, initDB()
}

func (db *DB) Store(n *drmtree.Node) (string, error) {
	return store(n)
}

func (db *DB) Load(k string) (*drmtree.Node, error) {
	return load(k)
}

func (db *DB) Walk(fn func(k string, n *drmtree.Node) error) error {
	return walk(fn)
}

func (db *DB) Close() error {
	return nil
}

A database/fs.go => database/fs.go +127 -0
@@ 0,0 1,127 @@
package database

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"git.sr.ht/~emersion/drmdb/drmtree"
)

const Dir = "db"

var ErrStop = fmt.Errorf("drmdb: stop walking")

func generateKey(n *drmtree.Node) (string, error) {
	if n.Driver == nil || n.Device == nil {
		return "", fmt.Errorf("node is missing driver/device")
	}

	ver := &n.Driver.Version

	var b bytes.Buffer
	b.WriteString(n.Driver.Name)
	b.WriteByte(0)
	fmt.Fprintf(&b, "%v.%v.%v-%v", ver.Major, ver.Minor, ver.Patch, ver.Date)
	b.WriteByte(0)
	b.WriteString(n.Driver.Kernel.SysName)
	b.WriteByte(0)
	b.WriteString(n.Driver.Kernel.Release)
	b.WriteByte(0)
	switch dev := n.Device.DeviceData.(type) {
	case *drmtree.DevicePCI:
		b.WriteString("pci")
		b.WriteByte(0)
		fmt.Fprintf(&b, "%04X:%04X", dev.Vendor, dev.Device)
	case *drmtree.DevicePlatform:
		b.WriteString("platform")
		if len(dev.Compatible) == 0 {
			return "", fmt.Errorf("platform device is missing compatibility info")
		}
		for _, compat := range dev.Compatible {
			b.WriteByte(0)
			b.WriteString(compat)
		}
	default:
		return "", fmt.Errorf("device bus type %v not supported", n.Device.BusType)
	}

	sum := sha256.Sum256(b.Bytes())
	return hex.EncodeToString(sum[:])[:12], nil
}

func initDB() error {
	return os.MkdirAll(Dir, 0755)
}

func store(n *drmtree.Node) (string, error) {
	k, err := generateKey(n)
	if err != nil {
		return "", err
	}

	p := filepath.Join(Dir, k+".json")
	f, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
	if err != nil {
		if os.IsExist(err) {
			return "", fmt.Errorf("data has already been submitted")
		}
		return "", err
	}
	defer f.Close()

	if err := json.NewEncoder(f).Encode(n); err != nil {
		return "", err
	}

	return k, f.Close()
}

func load(k string) (*drmtree.Node, error) {
	p := filepath.Join(Dir, k+".json")
	f, err := os.Open(p)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var n drmtree.Node
	if err := json.NewDecoder(f).Decode(&n); err != nil {
		return nil, err
	}

	return &n, f.Close()
}

func walk(fn func(k string, n *drmtree.Node) error) error {
	files, err := ioutil.ReadDir(Dir)
	if err != nil {
		return err
	}

	for _, fi := range files {
		if !strings.HasSuffix(fi.Name(), ".json") {
			continue
		}
		k := strings.TrimSuffix(fi.Name(), ".json")

		n, err := load(k)
		if err != nil {
			return err
		}

		if err := fn(k, n); err == ErrStop {
			return nil
		} else if err != nil {
			return err
		}
	}

	return nil
}

M server.go => server.go +12 -12
@@ 59,11 59,11 @@ func loadPCIIDs() (vendors map[uint16]string, devices map[uint32]string, err err
func New() *echo.Echo {
	e := echo.New()

	if err := database.Init(); err != nil {
		e.Logger.Fatal("Failed to initialize database:", err)
	db, err := database.Open()
	if err != nil {
		e.Logger.Fatal("Failed to open database:", err)
	}

	var err error
	e.Renderer, err = loadTemplates()
	if err != nil {
		e.Logger.Fatal("Failed to load templates:", err)


@@ 91,7 91,7 @@ func New() *echo.Echo {

		success := false
		for name, n := range nodes {
			if key, err := database.Store(n); err != nil {
			if key, err := db.Store(n); err != nil {
				fmt.Fprintf(c.Response(), "%s: error: %v\n", name, err)
			} else {
				u := "https://" + c.Request().Host + "/devices/" + key


@@ 115,7 115,7 @@ func New() *echo.Echo {

	e.GET("/drivers", func(c echo.Context) error {
		var drivers []*drmtree.Driver
		err := walkLatest(walkLatestDriver, func(k string, n *drmtree.Node) error {
		err := walkLatest(db, walkLatestDriver, func(k string, n *drmtree.Node) error {
			drivers = append(drivers, n.Driver)
			return nil
		})


@@ 141,7 141,7 @@ func New() *echo.Echo {
		}

		var devices []deviceData
		err := walkLatest(walkLatestDevice, func(k string, n *drmtree.Node) error {
		err := walkLatest(db, walkLatestDevice, func(k string, n *drmtree.Node) error {
			if driverFilter != "" && n.Driver.Name != driverFilter {
				return nil
			}


@@ 188,7 188,7 @@ func New() *echo.Echo {
			raw = true
		}

		n, err := database.Load(key)
		n, err := db.Load(key)
		if err != nil {
			if os.IsNotExist(err) {
				return c.String(http.StatusNotFound, "no such device")


@@ 200,7 200,7 @@ func New() *echo.Echo {
			return c.JSON(http.StatusOK, n)
		} else {
			var altDevices []altDeviceData
			err := database.Walk(func(k string, alt *drmtree.Node) error {
			err := db.Walk(func(k string, alt *drmtree.Node) error {
				if k == key {
					return nil
				}


@@ 239,7 239,7 @@ func New() *echo.Echo {
		var drivers []string
		caps := make(map[string]map[string]*uint64)
		clientCaps := make(map[string]map[string]bool)
		err := walkLatest(walkLatestDriver, func(k string, n *drmtree.Node) error {
		err := walkLatest(db, walkLatestDriver, func(k string, n *drmtree.Node) error {
			drv := n.Driver.Name
			drivers = append(drivers, drv)



@@ 288,7 288,7 @@ func New() *echo.Echo {

		drivers := make(map[string]struct{})
		props := make(map[string]propertyData)
		err := database.Walk(func(k string, n *drmtree.Node) error {
		err := db.Walk(func(k string, n *drmtree.Node) error {
			drv := n.Driver.Name
			drivers[drv] = struct{}{}



@@ 332,7 332,7 @@ func New() *echo.Echo {
		found := false
		var spec interface{}
		drivers := make(map[string]bool)
		err := database.Walk(func(k string, n *drmtree.Node) error {
		err := db.Walk(func(k string, n *drmtree.Node) error {
			if _, ok := drivers[n.Driver.Name]; !ok {
				drivers[n.Driver.Name] = false
			}


@@ 418,7 418,7 @@ func New() *echo.Echo {
		planes := make(map[drm.PlaneType]int)
		drivers := make(map[string]int)
		formats := make(map[modifierAndFormat]formatData)
		err := walkLatest(walkLatestDevice, func(k string, n *drmtree.Node) error {
		err := walkLatest(db, walkLatestDevice, func(k string, n *drmtree.Node) error {
			if driverFilter != "" && n.Driver.Name != driverFilter {
				return nil
			}

M walk.go => walk.go +2 -2
@@ 55,14 55,14 @@ const (
	walkLatestDevice
)

func walkLatest(f walkLatestField, fn func(k string, n *drmtree.Node) error) error {
func walkLatest(db *database.DB, f walkLatestField, fn func(k string, n *drmtree.Node) error) error {
	type node struct {
		k string
		n *drmtree.Node
	}

	latest := make(map[string]node)
	err := database.Walk(func(k string, n *drmtree.Node) error {
	err := db.Walk(func(k string, n *drmtree.Node) error {
		var latestKey string
		switch f {
		case walkLatestDriver: