go-vise

Constrained Size Output Virtual Machine
Info | Log | Files | Refs | README | LICENSE

size.go (3560B)


      1 package render
      2 
      3 import (
      4 	"bytes"
      5 	"fmt"
      6 	"strings"
      7 )
      8 
      9 // Sizer splits dynamic contents into individual segments for browseable pages.
     10 type Sizer struct {
     11 	outputSize uint32 // maximum output for a single page.
     12 //	menuSize uint16 // actual menu size for the dynamic page being sized
     13 	memberSizes map[string]uint16 // individual byte sizes of all content to be rendered by template.
     14 	totalMemberSize uint32 // total byte size of all content to be rendered by template (sum of memberSizes)
     15 	crsrs []uint32 // byte offsets in the sink content for browseable pages indices.
     16 	sink string // sink symbol.
     17 }
     18 
     19 // NewSizer creates a new Sizer object with the given output size constraint.
     20 func NewSizer(outputSize uint32) *Sizer {
     21 	return &Sizer{
     22 		outputSize: outputSize,
     23 		memberSizes: make(map[string]uint16),
     24 	}
     25 }
     26 
     27 // WithMenuSize sets the size of the menu being used in the rendering context.
     28 //func(szr *Sizer) WithMenuSize(menuSize uint16) *Sizer {
     29 //	szr.menuSize = menuSize
     30 //	return szr
     31 //}
     32 
     33 // Set adds a content symbol in the state it will be used by the renderer.
     34 func(szr *Sizer) Set(key string, size uint16) error {
     35 	szr.memberSizes[key] = size
     36 	if size == 0 {
     37 		szr.sink = key
     38 	}
     39 	szr.totalMemberSize += uint32(size)
     40 	return nil
     41 }
     42 
     43 // Check audits whether the rendered string is within the output size constraint of the sizer.
     44 func(szr *Sizer) Check(s string) (uint32, bool) {
     45 	l := uint32(len(s))
     46 	if szr.outputSize > 0 {
     47 		if l > szr.outputSize {
     48 			logg.Infof("sized check fails", "length", l, "sizer", szr)
     49 			logg.Tracef("", "sizer contents", s)
     50 			return 0, false
     51 		}
     52 		l = szr.outputSize - l
     53 	}
     54 	return l, true
     55 }
     56 
     57 // String implements the String interface.
     58 //
     59 // It outputs a representation of the Sizer fit for debug output.
     60 func(szr *Sizer) String() string {
     61 //	var diff uint32
     62 //	if szr.outputSize > 0 {
     63 //		diff = szr.outputSize - szr.totalMemberSize - uint32(szr.menuSize)
     64 //	}
     65 //	return fmt.Sprintf("output: %v, member: %v, menu: %v, diff: %v", szr.outputSize, szr.totalMemberSize, szr.menuSize, diff)
     66 	return fmt.Sprintf("output: %v, member: %v", szr.outputSize, szr.totalMemberSize)
     67 }
     68 
     69 // Size gives the byte size of content for a single symbol.
     70 //
     71 // Fails if the symbol has not been registered using Set
     72 func(szr *Sizer) Size(s string) (uint16, error) {
     73 	r, ok := szr.memberSizes[s]
     74 	if !ok {
     75 		return 0, fmt.Errorf("unknown member: %s", s)
     76 	}
     77 	return r, nil
     78 }
     79 
     80 // Menusize returns the currently defined menu size.
     81 //func(szr *Sizer) MenuSize() uint16 {
     82 //	return szr.menuSize
     83 //}
     84 
     85 // AddCursor adds a pagination cursor for the paged sink content.
     86 func(szr *Sizer) AddCursor(c uint32) {
     87 	logg.Debugf("Added cursor", "offset", c)
     88 	szr.crsrs = append(szr.crsrs, c)
     89 }
     90 
     91 // GetAt the paged symbols for the current page index.
     92 //
     93 // Fails if index requested is out of range.
     94 func(szr *Sizer) GetAt(values map[string]string, idx uint16) (map[string]string, error) {
     95 	if szr.sink == "" {
     96 		return values, nil
     97 	}
     98 	outValues := make(map[string]string)
     99 	for k, v := range values {
    100 		logg.Tracef("check values", "k", k, "v", v, "idx", idx, "cursors", szr.crsrs)
    101 		if szr.sink == k {
    102 			if idx >= uint16(len(szr.crsrs)) {
    103 				return nil, fmt.Errorf("no more values in index") 
    104 			}
    105 			c := szr.crsrs[idx]
    106 			v = v[c:]
    107 			nl := strings.Index(v, "\n")
    108 			if nl > 0 {
    109 				v = v[:nl]
    110 			}
    111 			b := bytes.ReplaceAll([]byte(v), []byte{0x00}, []byte{0x0a})
    112 			v = string(b)
    113 		}
    114 		outValues[k] = v
    115 	}
    116 	return outValues, nil
    117 }
    118 
    119 // Reset flushes all size measurements, making the sizer available for reuse.
    120 func(szr *Sizer) Reset() {
    121 	szr.crsrs = []uint32{}
    122 }