WIP Add size checker to resource render
This commit is contained in:
parent
397985f1ae
commit
4a91439320
@ -16,7 +16,7 @@ var (
|
|||||||
inputRegexStr = "^[a-zA-Z0-9].*$"
|
inputRegexStr = "^[a-zA-Z0-9].*$"
|
||||||
inputRegex = regexp.MustCompile(inputRegexStr)
|
inputRegex = regexp.MustCompile(inputRegexStr)
|
||||||
)
|
)
|
||||||
//
|
|
||||||
//type Config struct {
|
//type Config struct {
|
||||||
// FlagCount uint32
|
// FlagCount uint32
|
||||||
// CacheSize uint32
|
// CacheSize uint32
|
||||||
|
@ -23,17 +23,13 @@ func NewFsResource(path string) (FsResource) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func(fs FsResource) GetTemplate(sym string) (string, error) {
|
func(fs FsResource) GetTemplate(sym string, sizer *Sizer) (string, error) {
|
||||||
fp := path.Join(fs.Path, sym)
|
fp := path.Join(fs.Path, sym)
|
||||||
r, err := ioutil.ReadFile(fp)
|
r, err := ioutil.ReadFile(fp)
|
||||||
s := string(r)
|
s := string(r)
|
||||||
return strings.TrimSpace(s), err
|
return strings.TrimSpace(s), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func(fs FsResource) RenderTemplate(sym string, values map[string]string) (string, error) {
|
|
||||||
return DefaultRenderTemplate(&fs, sym, values)
|
|
||||||
}
|
|
||||||
|
|
||||||
func(fs FsResource) GetCode(sym string) ([]byte, error) {
|
func(fs FsResource) GetCode(sym string) ([]byte, error) {
|
||||||
fb := sym + ".bin"
|
fb := sym + ".bin"
|
||||||
fp := path.Join(fs.Path, fb)
|
fp := path.Join(fs.Path, fb)
|
||||||
|
@ -5,9 +5,10 @@ import (
|
|||||||
"text/template"
|
"text/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// DefaultRenderTemplate is an adapter to implement the builtin golang text template renderer as resource.RenderTemplate.
|
// DefaultRenderTemplate is an adapter to implement the builtin golang text template renderer as resource.RenderTemplate.
|
||||||
func DefaultRenderTemplate(r Resource, sym string, values map[string]string) (string, error) {
|
func DefaultRenderTemplate(r Resource, sym string, values map[string]string) (string, error) {
|
||||||
v, err := r.GetTemplate(sym)
|
v, err := r.GetTemplate(sym, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -23,4 +24,3 @@ func DefaultRenderTemplate(r Resource, sym string, values map[string]string) (st
|
|||||||
}
|
}
|
||||||
return b.String(), err
|
return b.String(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,16 +8,20 @@ import (
|
|||||||
|
|
||||||
// EntryFunc is a function signature for retrieving value for a key
|
// EntryFunc is a function signature for retrieving value for a key
|
||||||
type EntryFunc func(ctx context.Context) (string, error)
|
type EntryFunc func(ctx context.Context) (string, error)
|
||||||
|
type CodeFunc func(sym string) ([]byte, error)
|
||||||
|
type TemplateFunc func(sym string, sizer *Sizer) (string, error)
|
||||||
|
type FuncForFunc func(sym string) (EntryFunc, error)
|
||||||
|
|
||||||
// Resource implementation are responsible for retrieving values and templates for symbols, and can render templates from value dictionaries.
|
// Resource implementation are responsible for retrieving values and templates for symbols, and can render templates from value dictionaries.
|
||||||
type Resource interface {
|
type Resource interface {
|
||||||
GetTemplate(sym string) (string, error) // Get the template for a given symbol.
|
GetTemplate(sym string, sizer *Sizer) (string, error) // Get the template for a given symbol.
|
||||||
GetCode(sym string) ([]byte, error) // Get the bytecode for the given symbol.
|
GetCode(sym string) ([]byte, error) // Get the bytecode for the given symbol.
|
||||||
PutMenu(string, string) error // Add a menu item.
|
PutMenu(string, string) error // Add a menu item.
|
||||||
ShiftMenu() (string, string, error) // Remove and return the first menu item in list.
|
ShiftMenu() (string, string, error) // Remove and return the first menu item in list.
|
||||||
SetMenuBrowse(string, string, bool) error // Set menu browser display details.
|
SetMenuBrowse(string, string, bool) error // Set menu browser display details.
|
||||||
RenderTemplate(sym string, values map[string]string) (string, error) // Render the given data map using the template of the symbol.
|
RenderTemplate(sym string, values map[string]string) (string, error) // Render the given data map using the template of the symbol.
|
||||||
RenderMenu() (string, error) // Render the current state of menu
|
RenderMenu() (string, error) // Render the current state of menu
|
||||||
|
Render(sym string, values map[string]string, sizer *Sizer) (string, error) // Render full output.
|
||||||
FuncFor(sym string) (EntryFunc, error) // Resolve symbol content point for.
|
FuncFor(sym string) (EntryFunc, error) // Resolve symbol content point for.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,6 +29,28 @@ type MenuResource struct {
|
|||||||
menu [][2]string
|
menu [][2]string
|
||||||
next [2]string
|
next [2]string
|
||||||
prev [2]string
|
prev [2]string
|
||||||
|
codeFunc CodeFunc
|
||||||
|
templateFunc TemplateFunc
|
||||||
|
funcFunc FuncForFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMenuResource() *MenuResource {
|
||||||
|
return &MenuResource{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func(m *MenuResource) WithCodeGetter(codeGetter CodeFunc) *MenuResource {
|
||||||
|
m.codeFunc = codeGetter
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func(m *MenuResource) WithEntryFuncGetter(entryFuncGetter FuncForFunc) *MenuResource {
|
||||||
|
m.funcFunc = entryFuncGetter
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func(m *MenuResource) WithTemplateGetter(templateGetter TemplateFunc) *MenuResource {
|
||||||
|
m.templateFunc = templateGetter
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMenuBrowse defines the how pagination menu options should be displayed.
|
// SetMenuBrowse defines the how pagination menu options should be displayed.
|
||||||
@ -79,3 +105,44 @@ func(m *MenuResource) RenderMenu() (string, error) {
|
|||||||
}
|
}
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func(m *MenuResource) RenderTemplate(sym string, values map[string]string) (string, error) {
|
||||||
|
return DefaultRenderTemplate(m, sym, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
func(m *MenuResource) FuncFor(sym string) (EntryFunc, error) {
|
||||||
|
return m.funcFunc(sym)
|
||||||
|
}
|
||||||
|
|
||||||
|
func(m *MenuResource) GetCode(sym string) ([]byte, error) {
|
||||||
|
return m.codeFunc(sym)
|
||||||
|
}
|
||||||
|
|
||||||
|
func(m *MenuResource) GetTemplate(sym string, sizer *Sizer) (string, error) {
|
||||||
|
return m.templateFunc(sym, sizer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func(m *MenuResource) Render(sym string, values map[string]string, sizer *Sizer) (string, error) {
|
||||||
|
r := ""
|
||||||
|
s, err := m.RenderTemplate(sym, values)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
log.Printf("rendered %v bytes for template", len(s))
|
||||||
|
r += s
|
||||||
|
l, ok := sizer.Check(r)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("rendered size %v exceeds limits: %v", l, sizer)
|
||||||
|
}
|
||||||
|
s, err = m.RenderMenu()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
log.Printf("rendered %v bytes for menu", len(s))
|
||||||
|
r += s
|
||||||
|
l, ok = sizer.Check(r)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("rendered size %v exceeds limits: %v", l, sizer)
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
9
go/resource/resource_test.go
Normal file
9
go/resource/resource_test.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRenderLimit(t *testing.T) {
|
||||||
|
|
||||||
|
}
|
53
go/resource/size.go
Normal file
53
go/resource/size.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"git.defalsify.org/festive/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Sizer struct {
|
||||||
|
outputSize uint32
|
||||||
|
menuSize uint16
|
||||||
|
memberSizes map[string]uint16
|
||||||
|
totalMemberSize uint32
|
||||||
|
sink string
|
||||||
|
}
|
||||||
|
|
||||||
|
func SizerFromState(st *state.State) (Sizer, error){
|
||||||
|
sz := Sizer{
|
||||||
|
outputSize: st.GetOutputSize(),
|
||||||
|
menuSize: st.GetMenuSize(),
|
||||||
|
memberSizes: make(map[string]uint16),
|
||||||
|
}
|
||||||
|
sizes, err := st.Sizes()
|
||||||
|
if err != nil {
|
||||||
|
return sz, err
|
||||||
|
}
|
||||||
|
for k, v := range sizes {
|
||||||
|
if v == 0 {
|
||||||
|
sz.sink = k
|
||||||
|
}
|
||||||
|
sz.memberSizes[k] = v
|
||||||
|
sz.totalMemberSize += uint32(v)
|
||||||
|
}
|
||||||
|
return sz, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func(szr *Sizer) Check(s string) (uint32, bool) {
|
||||||
|
l := uint32(len(s))
|
||||||
|
if szr.outputSize > 0 && l > szr.outputSize {
|
||||||
|
log.Printf("sizer check fails with length %v: %s", l, szr)
|
||||||
|
return l, false
|
||||||
|
}
|
||||||
|
return l, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func(szr *Sizer) String() string {
|
||||||
|
var diff uint32
|
||||||
|
if szr.outputSize > 0 {
|
||||||
|
diff = szr.outputSize - szr.totalMemberSize - uint32(szr.menuSize)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("output: %v, member: %v, menu: %v, diff: %v", szr.outputSize, szr.totalMemberSize, szr.menuSize, diff)
|
||||||
|
}
|
96
go/resource/size_test.go
Normal file
96
go/resource/size_test.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.defalsify.org/festive/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestSizeResource struct {
|
||||||
|
*MenuResource
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTemplate(sym string, sizer *Sizer) (string, error) {
|
||||||
|
var tpl string
|
||||||
|
switch sym {
|
||||||
|
case "small":
|
||||||
|
tpl = "one {{.foo}} two {{.bar}} three {{.baz}}"
|
||||||
|
case "toobig":
|
||||||
|
tpl = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus in mattis lorem. Aliquam erat volutpat. Ut vitae metus."
|
||||||
|
|
||||||
|
}
|
||||||
|
return tpl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func funcFor(sym string) (EntryFunc, error) {
|
||||||
|
switch sym {
|
||||||
|
case "foo":
|
||||||
|
return getFoo, nil
|
||||||
|
case "bar":
|
||||||
|
return getBar, nil
|
||||||
|
case "baz":
|
||||||
|
return getBaz, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unknown func: %s", sym)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFoo(ctx context.Context) (string, error) {
|
||||||
|
return "inky", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBar(ctx context.Context) (string, error) {
|
||||||
|
return "pinky", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBaz(ctx context.Context) (string, error) {
|
||||||
|
return "blinky", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestSizeLimit(t *testing.T) {
|
||||||
|
st := state.NewState(0).WithOutputSize(128)
|
||||||
|
mrs := NewMenuResource().WithEntryFuncGetter(funcFor).WithTemplateGetter(getTemplate)
|
||||||
|
rs := TestSizeResource{
|
||||||
|
mrs,
|
||||||
|
}
|
||||||
|
st.Add("foo", "inky", 4)
|
||||||
|
st.Add("bar", "pinky", 10)
|
||||||
|
st.Add("baz", "blinky", 0)
|
||||||
|
st.Map("foo")
|
||||||
|
st.Map("bar")
|
||||||
|
st.Map("baz")
|
||||||
|
st.SetMenuSize(32)
|
||||||
|
szr, err := SizerFromState(&st)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rs.PutMenu("1", "foo the foo")
|
||||||
|
rs.PutMenu("2", "go to bar")
|
||||||
|
|
||||||
|
tpl, err := rs.GetTemplate("small", &szr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vals, err := st.Get()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_ = tpl
|
||||||
|
|
||||||
|
_, err = rs.Render("small", vals, &szr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rs.PutMenu("1", "foo the foo")
|
||||||
|
rs.PutMenu("2", "go to bar")
|
||||||
|
|
||||||
|
_, err = rs.Render("toobig", vals, &szr)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected size exceeded")
|
||||||
|
}
|
||||||
|
}
|
@ -28,13 +28,15 @@ type State struct {
|
|||||||
CacheUseSize uint32 // Currently used bytes by all values (not code) in cache
|
CacheUseSize uint32 // Currently used bytes by all values (not code) in cache
|
||||||
Cache []map[string]string // All loaded cache items
|
Cache []map[string]string // All loaded cache items
|
||||||
CacheMap map[string]string // Mapped
|
CacheMap map[string]string // Mapped
|
||||||
|
menuSize uint16 // Max size of menu
|
||||||
|
outputSize uint32 // Max size of output
|
||||||
input []byte // Last input
|
input []byte // Last input
|
||||||
code []byte // Pending bytecode to execute
|
code []byte // Pending bytecode to execute
|
||||||
execPath []string // Command symbols stack
|
execPath []string // Command symbols stack
|
||||||
arg *string // Optional argument. Nil if not set.
|
arg *string // Optional argument. Nil if not set.
|
||||||
sizes map[string]uint16 // Size limits for all loaded symbols.
|
sizes map[string]uint16 // Size limits for all loaded symbols.
|
||||||
sink *string // Sink symbol set for level
|
|
||||||
bitSize uint32 // size of (32-bit capacity) bit flag byte array
|
bitSize uint32 // size of (32-bit capacity) bit flag byte array
|
||||||
|
sink *string
|
||||||
//sizeIdx uint16
|
//sizeIdx uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,6 +197,12 @@ func(st State) WithCacheSize(cacheSize uint32) State {
|
|||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithCacheSize applies a cumulative cache size limitation for all cached items.
|
||||||
|
func(st State) WithOutputSize(outputSize uint32) State {
|
||||||
|
st.outputSize = outputSize
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
|
||||||
// Where returns the current active rendering symbol.
|
// Where returns the current active rendering symbol.
|
||||||
func(st State) Where() string {
|
func(st State) Where() string {
|
||||||
if len(st.execPath) == 0 {
|
if len(st.execPath) == 0 {
|
||||||
@ -268,7 +276,7 @@ func(st *State) Add(key string, value string, sizeLimit uint16) error {
|
|||||||
if sz == 0 {
|
if sz == 0 {
|
||||||
return fmt.Errorf("Cache capacity exceeded %v of %v", st.CacheUseSize + sz, st.CacheSize)
|
return fmt.Errorf("Cache capacity exceeded %v of %v", st.CacheUseSize + sz, st.CacheSize)
|
||||||
}
|
}
|
||||||
log.Printf("add key %s value size %v", key, sz)
|
log.Printf("add key %s value size %v limit %v", key, sz, sizeLimit)
|
||||||
st.Cache[len(st.Cache)-1][key] = value
|
st.Cache[len(st.Cache)-1][key] = value
|
||||||
st.CacheUseSize += sz
|
st.CacheUseSize += sz
|
||||||
st.sizes[key] = sizeLimit
|
st.sizes[key] = sizeLimit
|
||||||
@ -346,6 +354,42 @@ func(st *State) Get() (map[string]string, error) {
|
|||||||
return st.Cache[len(st.Cache)-1], nil
|
return st.Cache[len(st.Cache)-1], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func(st *State) Sizes() (map[string]uint16, error) {
|
||||||
|
if len(st.Cache) == 0 {
|
||||||
|
return nil, fmt.Errorf("get at top frame")
|
||||||
|
}
|
||||||
|
sizes := make(map[string]uint16)
|
||||||
|
var haveSink bool
|
||||||
|
for k, _ := range st.CacheMap {
|
||||||
|
l, ok := st.sizes[k]
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("missing size for %v", k))
|
||||||
|
}
|
||||||
|
if l == 0 {
|
||||||
|
if haveSink {
|
||||||
|
panic(fmt.Sprintf("duplicate sink for %v", k))
|
||||||
|
}
|
||||||
|
haveSink = true
|
||||||
|
}
|
||||||
|
sizes[k] = l
|
||||||
|
}
|
||||||
|
return sizes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func(st *State) SetMenuSize(size uint16) error {
|
||||||
|
st.menuSize = size
|
||||||
|
log.Printf("menu size changed to %v", st.menuSize)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func(st *State) GetMenuSize() uint16 {
|
||||||
|
return st.menuSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func(st *State) GetOutputSize() uint32 {
|
||||||
|
return st.outputSize
|
||||||
|
}
|
||||||
|
|
||||||
// Val returns value for key
|
// Val returns value for key
|
||||||
//
|
//
|
||||||
// Fails if key is not mapped.
|
// Fails if key is not mapped.
|
||||||
@ -372,7 +416,7 @@ func(st *State) Check(key string) bool {
|
|||||||
return st.frameOf(key) == -1
|
return st.frameOf(key) == -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns size used by values, and remaining size available
|
// Size returns size used by values and menu, and remaining size available
|
||||||
func(st *State) Size() (uint32, uint32) {
|
func(st *State) Size() (uint32, uint32) {
|
||||||
var l int
|
var l int
|
||||||
var c uint16
|
var c uint16
|
||||||
@ -381,6 +425,7 @@ func(st *State) Size() (uint32, uint32) {
|
|||||||
c += st.sizes[k]
|
c += st.sizes[k]
|
||||||
}
|
}
|
||||||
r := uint32(l)
|
r := uint32(l)
|
||||||
|
r += uint32(st.menuSize)
|
||||||
return r, uint32(c)-r
|
return r, uint32(c)-r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ type TestStatefulResolver struct {
|
|||||||
state *state.State
|
state *state.State
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *TestResource) GetTemplate(sym string) (string, error) {
|
func (r *TestResource) GetTemplate(sym string, sizer *resource.Sizer) (string, error) {
|
||||||
switch sym {
|
switch sym {
|
||||||
case "foo":
|
case "foo":
|
||||||
return "inky pinky blinky clyde", nil
|
return "inky pinky blinky clyde", nil
|
||||||
|
Loading…
Reference in New Issue
Block a user