package codec import ( "bytes" "encoding/binary" "fmt" "io" "math" "golang.org/x/crypto/ssh" ) func init() { Register("binray", func() Codec { return binaryCodec{order: binary.NativeEndian} }) Register("be", func() Codec { return binaryCodec{order: binary.BigEndian} }) Register("le", func() Codec { return binaryCodec{order: binary.LittleEndian} }) Register("ssh", func() Codec { return sshCodec{} }) } type binaryCodec struct { order binary.ByteOrder scratch [16]byte } func (binaryCodec) Type() string { return "binary" } func (codec binaryCodec) Encode(value any) ([]byte, error) { switch value := value.(type) { case bool: if value { return []byte{1}, nil } return []byte{0}, nil case *bool: if *value { return []byte{1}, nil } return []byte{0}, nil case int: codec.order.PutUint64(codec.scratch[:], uint64(value)) return codec.scratch[:8], nil case *int: return codec.Encode(*value) case int8: return []byte{uint8(value)}, nil case *int8: return []byte{uint8(*value)}, nil case int16: codec.order.PutUint16(codec.scratch[:], uint16(value)) return codec.scratch[:2], nil case *int16: return codec.Encode(*value) case int32: codec.order.PutUint32(codec.scratch[:], uint32(value)) return codec.scratch[:4], nil case *int32: return codec.Encode(*value) case int64: codec.order.PutUint64(codec.scratch[:], uint64(value)) return codec.scratch[:8], nil case *int64: return codec.Encode(*value) case uint: codec.order.PutUint64(codec.scratch[:], uint64(value)) return codec.scratch[:8], nil case *uint: return codec.Encode(*value) case uint8: return []byte{value}, nil case *uint8: return []byte{*value}, nil case uint16: codec.order.PutUint16(codec.scratch[:], value) return codec.scratch[:2], nil case *uint16: return codec.Encode(*value) case uint32: codec.order.PutUint32(codec.scratch[:], value) return codec.scratch[:4], nil case *uint32: return codec.Encode(*value) case uint64: codec.order.PutUint64(codec.scratch[:], value) return codec.scratch[:8], nil case *uint64: return codec.Encode(*value) case float32: codec.order.PutUint32(codec.scratch[:], math.Float32bits(value)) return codec.scratch[:4], nil case *float32: codec.order.PutUint32(codec.scratch[:], math.Float32bits(*value)) return codec.scratch[:4], nil case float64: codec.order.PutUint64(codec.scratch[:], math.Float64bits(value)) return codec.scratch[:8], nil case *float64: codec.order.PutUint64(codec.scratch[:], math.Float64bits(*value)) return codec.scratch[:8], nil case complex64: codec.order.PutUint32(codec.scratch[0:], math.Float32bits(real(value))) codec.order.PutUint32(codec.scratch[4:], math.Float32bits(imag(value))) return codec.scratch[:8], nil case *complex64: codec.order.PutUint32(codec.scratch[0:], math.Float32bits(real(*value))) codec.order.PutUint32(codec.scratch[4:], math.Float32bits(imag(*value))) return codec.scratch[:8], nil case complex128: codec.order.PutUint64(codec.scratch[0:], math.Float64bits(real(value))) codec.order.PutUint64(codec.scratch[4:], math.Float64bits(imag(value))) return codec.scratch[:16], nil case *complex128: codec.order.PutUint64(codec.scratch[0:], math.Float64bits(real(*value))) codec.order.PutUint64(codec.scratch[4:], math.Float64bits(imag(*value))) return codec.scratch[:16], nil case string: n := binary.PutUvarint(codec.scratch[:], uint64(len(value))) return append(codec.scratch[:n], []byte(value)...), nil case *string: n := binary.PutUvarint(codec.scratch[:], uint64(len(*value))) return append(codec.scratch[:n], []byte(*value)...), nil case []byte: n := binary.PutUvarint(codec.scratch[:], uint64(len(value))) return append(codec.scratch[:n], value...), nil case *[]byte: n := binary.PutUvarint(codec.scratch[:], uint64(len(*value))) return append(codec.scratch[:n], *value...), nil default: return nil, fmt.Errorf("codec: don't know how to binary encode %T", value) } } func (codec binaryCodec) Decode(data []byte, value any) error { switch value := value.(type) { case *bool: if len(data) < 1 { return io.ErrUnexpectedEOF } *value = data[0] != 0 case *int: if len(data) < 8 { return io.ErrUnexpectedEOF } *value = int(codec.order.Uint64(data)) case *int8: if len(data) < 1 { return io.ErrUnexpectedEOF } *value = int8(data[0]) case *int16: if len(data) < 2 { return io.ErrUnexpectedEOF } *value = int16(codec.order.Uint16(data)) case *int32: if len(data) < 4 { return io.ErrUnexpectedEOF } *value = int32(codec.order.Uint32(data)) case *int64: if len(data) < 8 { return io.ErrUnexpectedEOF } *value = int64(codec.order.Uint64(data)) case *uint: if len(data) < 8 { return io.ErrUnexpectedEOF } *value = uint(codec.order.Uint64(data)) case *uint8: if len(data) < 1 { return io.ErrUnexpectedEOF } *value = data[0] case *uint16: if len(data) < 1 { return io.ErrUnexpectedEOF } *value = codec.order.Uint16(data) case *uint32: if len(data) < 4 { return io.ErrUnexpectedEOF } *value = codec.order.Uint32(data) case *uint64: if len(data) < 8 { return io.ErrUnexpectedEOF } *value = codec.order.Uint64(data) case *float32: if len(data) < 4 { return io.ErrUnexpectedEOF } *value = math.Float32frombits(codec.order.Uint32(data)) case *float64: if len(data) < 8 { return io.ErrUnexpectedEOF } *value = math.Float64frombits(codec.order.Uint64(data)) case *complex64: if len(data) < 8 { return io.ErrUnexpectedEOF } *value = complex( math.Float32frombits(codec.order.Uint32(data[0:])), math.Float32frombits(codec.order.Uint32(data[4:])), ) case *complex128: if len(data) < 16 { return io.ErrUnexpectedEOF } *value = complex( math.Float64frombits(codec.order.Uint64(data[0:])), math.Float64frombits(codec.order.Uint64(data[8:])), ) case *string: r := bytes.NewReader(data) n, err := binary.ReadUvarint(r) if err != nil { return err } if uint64(r.Len()) < n { return io.ErrUnexpectedEOF } *value = string(data[len(data)-r.Len():]) case *[]byte: r := bytes.NewReader(data) n, err := binary.ReadUvarint(r) if err != nil { return err } if uint64(r.Len()) < n { return io.ErrUnexpectedEOF } copy(*value, data[len(data)-r.Len():]) default: return fmt.Errorf("codec: don't know how to binary decode %T", value) } return nil } type sshCodec struct{} func (sshCodec) Type() string { return "ssh" } func (sshCodec) Encode(value any) ([]byte, error) { return ssh.Marshal(value), nil } func (sshCodec) Decode(data []byte, value any) error { return ssh.Unmarshal(data, value) }