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 }