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 }