tape: Assorted changes i forgor

This commit is contained in:
Sasha Koshka 2025-09-26 00:20:53 -04:00
parent 84b96ed8f3
commit 7df18f7d26

View File

@ -8,6 +8,17 @@ package tape
// TODO: test all of these smaller functions individually
// For an explanation as to why this package always treats LBA/SBA as strings,
// refer to https://go.dev/blog/strings:
//
// Its important to state right up front that a string holds arbitrary
// bytes. It is not required to hold Unicode text, UTF-8 text, or any other
// predefined format. As far as the content of a string is concerned, it is
// exactly equivalent to a slice of bytes.
//
// Arbitrary byte slices and blobs won't be as common of a use case as text
// data, and if you need that anyway you can just cast it to a byte slice.
import "fmt"
import "reflect"
@ -142,7 +153,7 @@ func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n i
switch tag.WithoutCN() {
case SI:
// SI: (none)
setInt(destination, uint64(tag.CN()))
setUint(destination, uint64(tag.CN()), 1)
case LI:
// LI: <value: IntN>
nn, err := decodeAndSetUint(decoder, destination, tag.CN() + 1)
@ -164,7 +175,7 @@ func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n i
buffer := make([]byte, length)
nn, err := decoder.Read(buffer)
n += nn; if err != nil { return n, err }
setByteArray(destination, buffer)
setString(destination, string(buffer))
case LBA:
// LBA: <length: UN> <data: U8>*
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
@ -175,7 +186,7 @@ func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n i
buffer := make([]byte, length)
nn, err = decoder.Read(buffer)
n += nn; if err != nil { return n, err }
setByteArray(destination, buffer)
setString(destination, string(buffer))
case OTA:
// OTA: <length: UN> <elementTag: tape.Tag> <values>*
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
@ -325,6 +336,10 @@ func encodeAnyMap(encoder *Encoder, value any, tag Tag) (n int, err error) {
}
func canSet(destination reflect.Type, tag Tag) error {
// anything can be assigned to `any`
if isTypeAny(destination) {
return nil
}
switch tag.WithoutCN() {
case SI, LI, LSI:
switch destination.Kind() {
@ -362,17 +377,43 @@ func canSet(destination reflect.Type, tag Tag) error {
}
// setInt expects a settable destination.
func setInt[T int64 | uint64](destination reflect.Value, value T) {
func setInt(destination reflect.Value, value int64, bytes int) {
switch {
case destination.CanInt():
destination.Set(reflect.ValueOf(int64(value)).Convert(destination.Type()))
case destination.CanUint():
destination.Set(reflect.ValueOf(value).Convert(destination.Type()))
case isTypeAny(destination.Type()):
switch {
case bytes > 4: destination.Set(reflect.ValueOf(int64(value)))
case bytes > 2: destination.Set(reflect.ValueOf(int32(value)))
case bytes > 1: destination.Set(reflect.ValueOf(int16(value)))
default: destination.Set(reflect.ValueOf(int8(value)))
}
default:
panic("setInt called on an unsupported type")
}
}
// setUint expects a settable destination.
func setUint(destination reflect.Value, value uint64, bytes int) {
switch {
case destination.CanInt():
destination.Set(reflect.ValueOf(int64(value)).Convert(destination.Type()))
case destination.CanUint():
destination.Set(reflect.ValueOf(value).Convert(destination.Type()))
case isTypeAny(destination.Type()):
switch {
case bytes > 4: destination.Set(reflect.ValueOf(uint64(value)))
case bytes > 2: destination.Set(reflect.ValueOf(uint32(value)))
case bytes > 1: destination.Set(reflect.ValueOf(uint16(value)))
default: destination.Set(reflect.ValueOf(uint8(value)))
}
default:
panic("setUint called on an unsupported type")
}
}
// setFloat expects a settable destination.
func setFloat(destination reflect.Value, value float64) {
destination.Set(reflect.ValueOf(value).Convert(destination.Type()))
@ -387,7 +428,7 @@ func setByteArray(destination reflect.Value, value []byte) {
func decodeAndSetInt(decoder *Decoder, destination reflect.Value, bytes int) (n int, err error) {
value, nn, err := decoder.ReadIntN(bytes)
n += nn; if err != nil { return n, err }
setInt(destination, value)
setInt(destination, value, bytes)
return n, nil
}
@ -395,7 +436,7 @@ func decodeAndSetInt(decoder *Decoder, destination reflect.Value, bytes int) (n
func decodeAndSetUint(decoder *Decoder, destination reflect.Value, bytes int) (n int, err error) {
value, nn, err := decoder.ReadUintN(bytes)
n += nn; if err != nil { return n, err }
setInt(destination, value)
setUint(destination, value, bytes)
return n, nil
}
@ -452,8 +493,8 @@ func typeOf(decoder *Decoder, tag Tag) (reflect.Type, error) {
case 7: return reflect.TypeOf(float64(0)), nil
}
return nil, fmt.Errorf("unknown CN %d for FP", tag.CN())
case SBA: return reflect.SliceOf(reflect.TypeOf(byte(0))), nil
case LBA: return reflect.SliceOf(reflect.TypeOf(byte(0))), nil
case SBA: return reflect.TypeOf(""), nil
case LBA: return reflect.TypeOf(""), nil
case OTA:
elemTag, dimension, err := peekSlice(decoder, tag)
if err != nil { return nil, err }
@ -469,6 +510,12 @@ func typeOf(decoder *Decoder, tag Tag) (reflect.Type, error) {
return nil, fmt.Errorf("unknown TN %d", tag.TN())
}
// isTypeAny returns whether the given reflect.Type is an interface with no
// methods.
func isTypeAny(typ reflect.Type) bool {
return typ.Kind() == reflect.Interface && typ.NumMethod() == 0
}
// peekSlice returns the element tag and dimension count of the OTA currently
// being decoded. It does not use up the decoder, it only peeks.
func peekSlice(decoder *Decoder, tag Tag) (Tag, int, error) {