go-ipp/attribute.go

591 lines
12 KiB
Go

/*
Copyright 2022 Dolysis Consulting Limited
Licensed 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
http://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.
For changes see the git log
*/
package ipp
import (
"encoding/binary"
"fmt"
"io"
)
const (
sizeInteger = int16(4)
sizeBoolean = int16(1)
)
// AttributeEncoder encodes attribute to a io.Writer
type AttributeEncoder struct {
writer io.Writer
}
// NewAttributeEncoder returns a new encoder that writes to w
func NewAttributeEncoder(w io.Writer) *AttributeEncoder {
return &AttributeEncoder{w}
}
// Encode encodes a attribute and its value to a io.Writer
// the tag is determined by the AttributeTagMapping map
func (e *AttributeEncoder) Encode(attribute string, value interface{}) error {
tag, ok := AttributeTagMapping[attribute]
if !ok {
return fmt.Errorf("cannot get tag of attribute %s", attribute)
}
switch value.(type) {
case int:
if tag != TagInteger && tag != TagEnum {
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
}
if err := e.encodeTag(tag); err != nil {
return err
}
if err := e.encodeString(attribute); err != nil {
return err
}
if err := e.encodeInteger(int32(value.(int))); err != nil {
return err
}
case int16:
if tag != TagInteger && tag != TagEnum {
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
}
if err := e.encodeTag(tag); err != nil {
return err
}
if err := e.encodeString(attribute); err != nil {
return err
}
if err := e.encodeInteger(int32(value.(int16))); err != nil {
return err
}
case int8:
if tag != TagInteger && tag != TagEnum {
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
}
if err := e.encodeTag(tag); err != nil {
return err
}
if err := e.encodeString(attribute); err != nil {
return err
}
if err := e.encodeInteger(int32(value.(int8))); err != nil {
return err
}
case int32:
if tag != TagInteger && tag != TagEnum {
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
}
if err := e.encodeTag(tag); err != nil {
return err
}
if err := e.encodeString(attribute); err != nil {
return err
}
if err := e.encodeInteger(value.(int32)); err != nil {
return err
}
case int64:
if tag != TagInteger && tag != TagEnum {
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
}
if err := e.encodeTag(tag); err != nil {
return err
}
if err := e.encodeString(attribute); err != nil {
return err
}
if err := e.encodeInteger(int32(value.(int64))); err != nil {
return err
}
case []int:
if tag != TagInteger && tag != TagEnum {
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
}
for index, val := range value.([]int) {
if err := e.encodeTag(tag); err != nil {
return err
}
if index == 0 {
if err := e.encodeString(attribute); err != nil {
return err
}
} else {
if err := e.writeNullByte(); err != nil {
return err
}
}
if err := e.encodeInteger(int32(val)); err != nil {
return err
}
}
case []int16:
if tag != TagInteger && tag != TagEnum {
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
}
for index, val := range value.([]int16) {
if err := e.encodeTag(tag); err != nil {
return err
}
if index == 0 {
if err := e.encodeString(attribute); err != nil {
return err
}
} else {
if err := e.writeNullByte(); err != nil {
return err
}
}
if err := e.encodeInteger(int32(val)); err != nil {
return err
}
}
case []int8:
if tag != TagInteger && tag != TagEnum {
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
}
for index, val := range value.([]int8) {
if err := e.encodeTag(tag); err != nil {
return err
}
if index == 0 {
if err := e.encodeString(attribute); err != nil {
return err
}
} else {
if err := e.writeNullByte(); err != nil {
return err
}
}
if err := e.encodeInteger(int32(val)); err != nil {
return err
}
}
case []int32:
if tag != TagInteger && tag != TagEnum {
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
}
for index, val := range value.([]int32) {
if err := e.encodeTag(tag); err != nil {
return err
}
if index == 0 {
if err := e.encodeString(attribute); err != nil {
return err
}
} else {
if err := e.writeNullByte(); err != nil {
return err
}
}
if err := e.encodeInteger(val); err != nil {
return err
}
}
case []int64:
if tag != TagInteger && tag != TagEnum {
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
}
for index, val := range value.([]int64) {
if err := e.encodeTag(tag); err != nil {
return err
}
if index == 0 {
if err := e.encodeString(attribute); err != nil {
return err
}
} else {
if err := e.writeNullByte(); err != nil {
return err
}
}
if err := e.encodeInteger(int32(val)); err != nil {
return err
}
}
case bool:
if tag != TagBoolean {
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
}
if err := e.encodeTag(tag); err != nil {
return err
}
if err := e.encodeString(attribute); err != nil {
return err
}
if err := e.encodeBoolean(value.(bool)); err != nil {
return err
}
case []bool:
if tag != TagBoolean {
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
}
for index, val := range value.([]bool) {
if err := e.encodeTag(tag); err != nil {
return err
}
if index == 0 {
if err := e.encodeString(attribute); err != nil {
return err
}
} else {
if err := e.writeNullByte(); err != nil {
return err
}
}
if err := e.encodeBoolean(val); err != nil {
return err
}
}
case string:
if err := e.encodeTag(tag); err != nil {
return err
}
if err := e.encodeString(attribute); err != nil {
return err
}
if err := e.encodeString(value.(string)); err != nil {
return err
}
case []string:
for index, val := range value.([]string) {
if err := e.encodeTag(tag); err != nil {
return err
}
if index == 0 {
if err := e.encodeString(attribute); err != nil {
return err
}
} else {
if err := e.writeNullByte(); err != nil {
return err
}
}
if err := e.encodeString(val); err != nil {
return err
}
}
default:
if tag != TagBeginCollection {
return fmt.Errorf("type %T is not supported", value)
}
if err := e.encodeTag(TagBeginCollection); err != nil {
return err
}
if err := e.encodeString(attribute); err != nil {
return err
}
if err := e.writeNullByte(); err != nil {
return err
}
for member, value := range value.(map[string]interface{}) {
if err := e.encodeTag(TagMemberName); err != nil {
return err
}
if err := e.writeNullByte(); err != nil {
return err
}
if err := e.encodeString(member); err != nil {
return err
}
if err := e.encodeTag(TagKeyword); err != nil {
return err
}
if err := e.writeNullByte(); err != nil {
return err
}
if err := e.encodeString(value.(string)); err != nil {
return err
}
}
if err := e.encodeTag(TagEndCollection); err != nil {
return err
}
if err := e.writeNullByte(); err != nil {
return err
}
if err := e.writeNullByte(); err != nil {
return err
}
}
return nil
}
func (e *AttributeEncoder) encodeString(s string) error {
if err := binary.Write(e.writer, binary.BigEndian, int16(len(s))); err != nil {
return err
}
_, err := e.writer.Write([]byte(s))
return err
}
func (e *AttributeEncoder) encodeInteger(i int32) error {
if err := binary.Write(e.writer, binary.BigEndian, sizeInteger); err != nil {
return err
}
return binary.Write(e.writer, binary.BigEndian, i)
}
func (e *AttributeEncoder) encodeBoolean(b bool) error {
if err := binary.Write(e.writer, binary.BigEndian, sizeBoolean); err != nil {
return err
}
return binary.Write(e.writer, binary.BigEndian, b)
}
func (e *AttributeEncoder) encodeTag(t int8) error {
return binary.Write(e.writer, binary.BigEndian, t)
}
func (e *AttributeEncoder) writeNullByte() error {
return binary.Write(e.writer, binary.BigEndian, int16(0))
}
// Attribute defines an ipp attribute
type Attribute struct {
Tag int8
Name string
Value interface{}
}
// Resolution defines the resolution attribute
type Resolution struct {
Height int32
Width int32
Depth int8
}
// AttributeDecoder reads and decodes ipp from an input stream
type AttributeDecoder struct {
reader io.Reader
}
// NewAttributeDecoder returns a new decoder that reads from r
func NewAttributeDecoder(r io.Reader) *AttributeDecoder {
return &AttributeDecoder{r}
}
// Decode reads the next ipp attribute into a attribute struct. the type is identified by a tag passed as an argument
func (d *AttributeDecoder) Decode(tag int8) (*Attribute, error) {
attr := Attribute{Tag: tag}
name, err := d.decodeString()
if err != nil {
return nil, err
}
attr.Name = name
switch attr.Tag {
case TagEnum, TagInteger:
val, err := d.decodeInteger()
if err != nil {
return nil, err
}
attr.Value = val
case TagBoolean:
val, err := d.decodeBool()
if err != nil {
return nil, err
}
attr.Value = val
case TagDate:
val, err := d.decodeDate()
if err != nil {
return nil, err
}
attr.Value = val
case TagRange:
val, err := d.decodeRange()
if err != nil {
return nil, err
}
attr.Value = val
case TagResolution:
val, err := d.decodeResolution()
if err != nil {
return nil, err
}
attr.Value = val
default:
val, err := d.decodeString()
if err != nil {
return nil, err
}
attr.Value = val
}
return &attr, nil
}
func (d *AttributeDecoder) decodeBool() (b bool, err error) {
if _, err = d.readValueLength(); err != nil {
return
}
if err = binary.Read(d.reader, binary.BigEndian, &b); err != nil {
return
}
return
}
func (d *AttributeDecoder) decodeInteger() (i int, err error) {
if _, err = d.readValueLength(); err != nil {
return
}
var reti int32
if err = binary.Read(d.reader, binary.BigEndian, &reti); err != nil {
return
}
return int(reti), nil
}
func (d *AttributeDecoder) decodeString() (string, error) {
length, err := d.readValueLength()
if err != nil {
return "", err
}
if length == 0 {
return "", nil
}
bs := make([]byte, length)
if _, err := d.reader.Read(bs); err != nil {
return "", nil
}
return string(bs), nil
}
func (d *AttributeDecoder) decodeDate() ([]int, error) {
length, err := d.readValueLength()
if err != nil {
return nil, err
}
is := make([]int, length)
var ti int8
for i := int16(0); i < length; i++ {
if err = binary.Read(d.reader, binary.BigEndian, &ti); err != nil {
return nil, err
}
is[i] = int(ti)
}
return is, nil
}
func (d *AttributeDecoder) decodeRange() ([]int32, error) {
length, err := d.readValueLength()
if err != nil {
return nil, err
}
// initialize range element count (c) and range slice (r)
c := length / 4
r := make([]int32, c)
for i := int16(0); i < c; i++ {
var ti int32
if err = binary.Read(d.reader, binary.BigEndian, &ti); err != nil {
return nil, err
}
r[i] = ti
}
return r, nil
}
func (d *AttributeDecoder) decodeResolution() (res Resolution, err error) {
_, err = d.readValueLength()
if err != nil {
return
}
if err = binary.Read(d.reader, binary.BigEndian, &res.Height); err != nil {
return
}
if err = binary.Read(d.reader, binary.BigEndian, &res.Width); err != nil {
return
}
if err = binary.Read(d.reader, binary.BigEndian, &res.Depth); err != nil {
return
}
return
}
func (d *AttributeDecoder) readValueLength() (length int16, err error) {
err = binary.Read(d.reader, binary.BigEndian, &length)
return
}