blob: 32ba6d32ae80694bdeee13c717749f134c167206 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package utils
import (
"container/list"
"context"
"encoding/binary"
"fmt"
"math/big"
"strconv"
"github.com/pkg/errors"
)
type WriteBufferBoxBased interface {
WriteBuffer
GetBox() AsciiBox
}
func NewWriteBufferBoxBased(opts ...func(buffer *boxedWriteBuffer)) WriteBufferBoxBased {
wb := &boxedWriteBuffer{
List: list.New(),
desiredWidth: 120,
currentWidth: 118,
asciiBoxWriter: AsciiBoxWriterDefault,
asciiBoxWriterLight: AsciiBoxWriterLight,
}
for _, opt := range opts {
opt(wb)
}
return wb
}
func WithWriteBufferBoxBasedMergeSingleBoxes() func(*boxedWriteBuffer) {
return func(wb *boxedWriteBuffer) {
wb.mergeSingleBoxes = true
}
}
func WithWriteBufferBoxBasedOmitEmptyBoxes() func(*boxedWriteBuffer) {
return func(wb *boxedWriteBuffer) {
wb.omitEmptyBoxes = true
}
}
func WithWriteBufferBoxBasedPrintPosLengthFooter() func(*boxedWriteBuffer) {
return func(wb *boxedWriteBuffer) {
wb.printPosLengthFooter = true
}
}
///////////////////////////////////////
///////////////////////////////////////
//
// Internal section
//
type boxedWriteBuffer struct {
BufferCommons
*list.List
desiredWidth int
currentWidth int
mergeSingleBoxes bool
omitEmptyBoxes bool
printPosLengthFooter bool
asciiBoxWriter AsciiBoxWriter
asciiBoxWriterLight AsciiBoxWriter
pos uint
}
var _ WriteBuffer = (*boxedWriteBuffer)(nil)
//
// Internal section
//
///////////////////////////////////////
///////////////////////////////////////
func (*boxedWriteBuffer) GetByteOrder() binary.ByteOrder {
return binary.BigEndian
}
func (*boxedWriteBuffer) SetByteOrder(_ binary.ByteOrder) {
}
func (b *boxedWriteBuffer) GetBox() AsciiBox {
back := b.Back()
if back == nil {
return AsciiBox{data: "<nil>"}
}
return back.Value.(AsciiBox)
}
func (b *boxedWriteBuffer) PushContext(_ string, _ ...WithWriterArgs) error {
b.currentWidth -= boxLineOverheat
b.PushBack(make([]AsciiBox, 0))
return nil
}
func (b *boxedWriteBuffer) GetPos() uint16 {
return uint16(b.pos / 8)
}
func (b *boxedWriteBuffer) WriteBit(logicalName string, value bool, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
asInt := 0
if value {
asInt = 1
}
stringValue := fmt.Sprintf("b%d %t%s", asInt, value, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(1)))
b.PushBack(box)
b.move(1)
return nil
}
func (b *boxedWriteBuffer) WriteByte(logicalName string, value byte, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
printSafeChar := value
if value < 32 || value > 126 {
printSafeChar = '.'
}
stringValue := fmt.Sprintf("%#02x '%c'%s", value, printSafeChar, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(8)))
b.PushBack(box)
b.move(8)
return nil
}
func (b *boxedWriteBuffer) WriteByteArray(logicalName string, data []byte, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
if additionalStringRepresentation != "" {
additionalStringRepresentation += "\n"
}
stringValue := fmt.Sprintf("%s%s", additionalStringRepresentation, Dump(data))
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(len(data)*8)))
b.PushBack(box)
b.move(uint(len(data) * 8))
return nil
}
func (b *boxedWriteBuffer) WriteUint8(logicalName string, bitLength uint8, value uint8, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
stringValue := fmt.Sprintf("%#0*x %d%s", bitLength/4, value, value, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(int(bitLength))))
b.PushBack(box)
b.move(uint(bitLength))
return nil
}
func (b *boxedWriteBuffer) WriteUint16(logicalName string, bitLength uint8, value uint16, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
stringValue := fmt.Sprintf("%#0*x %d%s", bitLength/4, value, value, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(int(bitLength))))
b.PushBack(box)
b.move(uint(bitLength))
return nil
}
func (b *boxedWriteBuffer) WriteUint32(logicalName string, bitLength uint8, value uint32, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
stringValue := fmt.Sprintf("%#0*x %d%s", bitLength/4, value, value, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(int(bitLength))))
b.PushBack(box)
b.move(uint(bitLength))
return nil
}
func (b *boxedWriteBuffer) WriteUint64(logicalName string, bitLength uint8, value uint64, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
stringValue := fmt.Sprintf("%#0*x %d%s", bitLength/4, value, value, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(int(bitLength))))
b.PushBack(box)
b.move(uint(bitLength))
return nil
}
func (b *boxedWriteBuffer) WriteInt8(logicalName string, bitLength uint8, value int8, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
stringValue := fmt.Sprintf("%#0*x %d%s", bitLength/4, value, value, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(int(bitLength))))
b.PushBack(box)
b.move(uint(bitLength))
return nil
}
func (b *boxedWriteBuffer) WriteInt16(logicalName string, bitLength uint8, value int16, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
stringValue := fmt.Sprintf("%#0*x %d%s", bitLength/4, value, value, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(int(bitLength))))
b.PushBack(box)
b.move(uint(bitLength))
return nil
}
func (b *boxedWriteBuffer) WriteInt32(logicalName string, bitLength uint8, value int32, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
stringValue := fmt.Sprintf("%#0*x %d%s", bitLength/4, value, value, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(int(bitLength))))
b.PushBack(box)
b.move(uint(bitLength))
return nil
}
func (b *boxedWriteBuffer) WriteInt64(logicalName string, bitLength uint8, value int64, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
stringValue := fmt.Sprintf("%#0*x %d%s", bitLength/4, value, value, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(int(bitLength))))
b.PushBack(box)
b.move(uint(bitLength))
return nil
}
func (b *boxedWriteBuffer) WriteBigInt(logicalName string, bitLength uint8, value *big.Int, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
stringValue := fmt.Sprintf("%#0*x %d%s", bitLength/4, value, value, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(int(bitLength))))
b.PushBack(box)
b.move(uint(bitLength))
return nil
}
func (b *boxedWriteBuffer) WriteFloat32(logicalName string, bitLength uint8, value float32, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
stringValue := fmt.Sprintf("%#0*x %f%s", bitLength/4, value, value, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(int(bitLength))))
b.PushBack(box)
b.move(uint(bitLength))
return nil
}
func (b *boxedWriteBuffer) WriteFloat64(logicalName string, bitLength uint8, value float64, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
stringValue := fmt.Sprintf("%#0*x %f%s", bitLength/4, value, value, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(int(bitLength))))
b.PushBack(box)
b.move(uint(bitLength))
return nil
}
func (b *boxedWriteBuffer) WriteBigFloat(logicalName string, bitLength uint8, value *big.Float, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
stringValue := fmt.Sprintf("%#0*x %f%s", bitLength/4, value, value, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(int(bitLength))))
b.PushBack(box)
b.move(uint(bitLength))
return nil
}
func (b *boxedWriteBuffer) WriteString(logicalName string, bitLength uint32, value string, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
stringValue := fmt.Sprintf("%s%s", value, additionalStringRepresentation)
box := b.asciiBoxWriter.BoxString(stringValue, WithAsciiBoxName(logicalName), WithAsciiBoxFooter(b.getPosFooter(int(bitLength))))
b.PushBack(box)
b.move(uint(bitLength))
return nil
}
func (b *boxedWriteBuffer) WriteVirtual(ctx context.Context, logicalName string, value any, writerArgs ...WithWriterArgs) error {
additionalStringRepresentation := b.extractAdditionalStringRepresentation(UpcastWriterArgs(writerArgs...)...)
if value == nil {
return nil
}
var asciiBox AsciiBox
switch value.(type) {
case bool:
stringValue := fmt.Sprintf("%t", value)
asciiBox = b.asciiBoxWriterLight.BoxString(stringValue, WithAsciiBoxName(logicalName))
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
stringValue := fmt.Sprintf("%#x %d%s", value, value, additionalStringRepresentation)
asciiBox = b.asciiBoxWriterLight.BoxString(stringValue, WithAsciiBoxName(logicalName))
case float32, float64:
stringValue := fmt.Sprintf("%x %f%s", value, value, additionalStringRepresentation)
asciiBox = b.asciiBoxWriterLight.BoxString(stringValue, WithAsciiBoxName(logicalName))
case Serializable:
virtualBoxedWriteBuffer := NewWriteBufferBoxBased().(*boxedWriteBuffer)
virtualBoxedWriteBuffer.mergeSingleBoxes = b.mergeSingleBoxes
virtualBoxedWriteBuffer.omitEmptyBoxes = b.omitEmptyBoxes
virtualBoxedWriteBuffer.printPosLengthFooter = b.printPosLengthFooter
if err := value.(Serializable).SerializeWithWriteBuffer(ctx, virtualBoxedWriteBuffer); err == nil {
asciiBox = b.asciiBoxWriterLight.BoxBox(virtualBoxedWriteBuffer.GetBox(), WithAsciiBoxName(logicalName))
} else {
b.asciiBoxWriterLight.BoxString(err.Error(), WithAsciiBoxName(logicalName))
}
default:
asciiBox = b.asciiBoxWriterLight.BoxString(fmt.Sprintf("%v%s", value, additionalStringRepresentation), WithAsciiBoxName(logicalName))
}
b.PushBack(asciiBox)
return nil
}
func (b *boxedWriteBuffer) WriteSerializable(ctx context.Context, serializable Serializable) error {
if serializable == nil {
return nil
}
currentPos := int(b.pos) // used for footer so we remember that before we advance
if err := serializable.SerializeWithWriteBuffer(ctx, b); err != nil {
return err
}
back := b.Back()
if back == nil {
return nil
}
if ab, ok := back.Value.(AsciiBox); ok {
if la, ok := serializable.(LengthAware); ok {
bitLength := int(la.GetLengthInBits(ctx))
back.Value = ab.ChangeBoxFooter(b.getPosFooterWithCurrentPost(currentPos, bitLength))
}
}
return nil
}
func (b *boxedWriteBuffer) PopContext(logicalName string, _ ...WithWriterArgs) error {
b.currentWidth += boxLineOverheat
finalBoxes := make([]AsciiBox, 0)
findTheBox:
for back := b.Back(); back != nil; back = b.Back() {
switch back.Value.(type) {
case AsciiBox:
asciiBox := b.Remove(back).(AsciiBox)
if b.omitEmptyBoxes && asciiBox.IsEmpty() {
continue
}
finalBoxes = append([]AsciiBox{asciiBox}, finalBoxes...)
case []AsciiBox:
b.Remove(back)
asciiBoxes := b.Remove(back).([]AsciiBox)
finalBoxes = append(asciiBoxes, finalBoxes...)
break findTheBox
default:
return errors.New("We should never reach this point")
}
}
if b.mergeSingleBoxes && len(finalBoxes) == 1 {
onlyChild := finalBoxes[0]
childName := onlyChild.GetBoxName()
onlyChild = onlyChild.ChangeBoxName(logicalName + "/" + childName)
if b.omitEmptyBoxes && onlyChild.IsEmpty() {
return nil
}
b.PushBack(onlyChild)
return nil
}
asciiBox := b.asciiBoxWriter.BoxBox(b.asciiBoxWriter.AlignBoxes(finalBoxes, b.currentWidth), WithAsciiBoxName(logicalName))
if b.omitEmptyBoxes && asciiBox.IsEmpty() {
return nil
}
b.PushBack(asciiBox)
return nil
}
func (b *boxedWriteBuffer) extractAdditionalStringRepresentation(readerWriterArgs ...WithReaderWriterArgs) string {
representation := b.BufferCommons.ExtractAdditionalStringRepresentation(readerWriterArgs...)
if representation != "" {
return " " + representation
}
return ""
}
func (b *boxedWriteBuffer) move(bits uint) {
b.pos += bits
}
func (b *boxedWriteBuffer) getPosFooter(bitLength int) string {
return b.getPosFooterWithCurrentPost(int(b.pos), bitLength)
}
func (b *boxedWriteBuffer) getPosFooterWithCurrentPost(currentPos, bitLength int) string {
if !b.printPosLengthFooter {
return ""
}
bytePos := currentPos / 8
bitRemainder := currentPos % 8
pos := strconv.Itoa(bytePos)
if bitRemainder != 0 {
pos += "." + strconv.Itoa(bitRemainder)
}
byteLength := bitLength / 8
bitLengthRemainder := bitLength % 8
length := strconv.Itoa(byteLength)
if bitLengthRemainder != 0 {
length += "." + strconv.Itoa(bitLengthRemainder)
}
return fmt.Sprintf("%s/%s", pos, length)
}