From 71fcac40f0ce440a145fffe984e784a13dd018ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=2C=20S=C3=B6lver?= Date: Wed, 9 Jun 2021 19:51:48 +0200 Subject: [PATCH] Simplify unmarshalling. --- packages/ipp/boolean.go | 34 +++++-- packages/ipp/charsetvalue.go | 7 -- packages/ipp/collection.hex | 94 ++++++++++++++++++ packages/ipp/enum.go | 13 ++- packages/ipp/integer.go | 20 ++-- packages/ipp/integer_test.go | 18 ++-- packages/ipp/keyword.go | 13 ++- packages/ipp/keyword_test.go | 49 +++++++++ packages/ipp/messages.go | 149 ++++++++++++++-------------- packages/ipp/mimemediatype.go | 10 +- packages/ipp/namewithoutlanguage.go | 7 -- packages/ipp/naturallanguage.go | 7 -- packages/ipp/rangeofinteger.go | 48 +++++++-- packages/ipp/rangeofinteger_test.go | 39 ++++++-- packages/ipp/setofintegers.go | 56 +++++++++++ packages/ipp/setofstrings.go | 60 ++++++----- packages/ipp/textwithoutlanguage.go | 7 -- 17 files changed, 452 insertions(+), 179 deletions(-) create mode 100644 packages/ipp/collection.hex create mode 100644 packages/ipp/setofintegers.go diff --git a/packages/ipp/boolean.go b/packages/ipp/boolean.go index 36b0945..25bda6b 100644 --- a/packages/ipp/boolean.go +++ b/packages/ipp/boolean.go @@ -36,9 +36,32 @@ func (b *Boolean) valueTag() tag { return booleanValueTag } +func unmarshalSingleAttribute(byteStream io.Reader) (string, []byte) { + var length uint16 + binary.Read(byteStream, binary.BigEndian, &length) + attributeName := make([]byte, length) + if length > 0 { + binary.Read(byteStream, binary.BigEndian, attributeName) + } + binary.Read(byteStream, binary.BigEndian, &length) + attributeValue := make([]byte, length) + binary.Read(byteStream, binary.BigEndian, attributeValue) + return string(attributeName), attributeValue +} + func (b *Boolean) unmarshal(byteStream io.Reader) { - name, data := unmarshalSingleAttribute(byteStream) - b.name = name + + var length uint16 + binary.Read(byteStream, binary.BigEndian, &length) + attributeName := make([]byte, length) + if length > 0 { + binary.Read(byteStream, binary.BigEndian, attributeName) + } + b.name = string(attributeName) + binary.Read(byteStream, binary.BigEndian, &length) + data := make([]byte, length) + binary.Read(byteStream, binary.BigEndian, data) + //name, data := unmarshalSingleAttribute(byteStream) if data[0] == 0 { b.value = false return @@ -60,9 +83,4 @@ func (b *Boolean) marshal() []byte { buf.WriteByte(byte(0)) } return buf.Bytes() -} - -func (b *Boolean) size() int { - l := 5 + len(b.name) - return l -} +} \ No newline at end of file diff --git a/packages/ipp/charsetvalue.go b/packages/ipp/charsetvalue.go index 6493901..3206f96 100644 --- a/packages/ipp/charsetvalue.go +++ b/packages/ipp/charsetvalue.go @@ -45,10 +45,3 @@ func (c *CharSetValue) marshal() []byte { func (c *CharSetValue) marshalInto([]byte) int { return 0 } - -func (c *CharSetValue) size() int { - l := 1 + 4 // The attribute tag + 2 lengths - l += len(c.name) - l += len(c.value) - return l -} diff --git a/packages/ipp/collection.hex b/packages/ipp/collection.hex new file mode 100644 index 0000000..4439461 --- /dev/null +++ b/packages/ipp/collection.hex @@ -0,0 +1,94 @@ + +34 +00 00 +00 11 // Name length +6d 65 64 69 61 2d 63 6f 6c 2d 64 65 66 61 75 6c 74 // "media-col-default" +00 00 // Always zero + +4a // Collection member +00 00 // Always zero +00 0a // Value length +6d 65 64 69 61 2d 74 79 70 65 // "media-type" +44 // Keyword +00 00 00 0a +73 74 61 74 69 6f 6e 65 72 79 // "stationery" + +4a // Collection member +00 00 // Always zero +00 0a // Value length +6d 65 64 69 61 2d 73 69 7a 65 // Member name "media-size" + +34 // Begin Collection +00 00 // Name Length (In this case there is no name) +00 00 // Always zero + +4a +00 00 +00 0b +78 2d 64 69 6d 65 6e 73 69 6f 6e // "x-dimension" +21 // Integer value +00 00 +00 04 +00 00 52 08 + +4a +00 00 +00 0b +79 2d 64 69 6d 65 6e 73 69 6f 6e // "y-dimension" +21 // Integer value +00 00 +00 04 +00 00 74 04 +37 00 00 00 00 // End + +4a +00 00 +00 13 +6d 65 64 69 61 2d 62 6f 74 74 6f 6d 2d 6d 61 72 67 69 6e // "media-bottom-margin" +21 +00 00 +00 04 +00 00 01 b0 + +4a +00 00 +00 11 +6d 65 64 69 61 2d 6c 65 66 74 2d 6d 61 72 67 69 6e +21 +00 00 +00 04 +00 00 01 b0 + +4a +00 00 +00 12 +6d 65 64 69 61 2d 72 69 67 68 74 2d 6d 61 72 67 69 6e // "media-right-margin" +21 00 00 00 04 00 00 01 b0 + +4a +00 00 +00 10 +6d 65 64 69 61 2d 74 6f 70 2d 6d 61 72 67 69 6e +21 00 00 00 04 00 00 01 b0 + +4a +00 00 +00 0c +6d 65 64 69 61 2d 73 6f 75 72 63 65 // "media-source" +44 // Keyword +00 00 +00 04 +61 75 74 6f // "auto" +37 00 00 00 00 // End + +23 00 1d 6f 72 69 65 6e 74 61 74 69 6f 6e 2d 72 65 + + + +From ipptool +media-col-default (collection) = +{media-type=stationery media-size={x-dimension=21000 y-dimension=29700} +media-bottom-margin=432 +media-left-margin=432 +media-right-margin=432 +media-top-margin=432 media-source=auto} \ No newline at end of file diff --git a/packages/ipp/enum.go b/packages/ipp/enum.go index df04e84..c322217 100644 --- a/packages/ipp/enum.go +++ b/packages/ipp/enum.go @@ -3,6 +3,7 @@ package ipp import ( + "bufio" "fmt" ) @@ -30,10 +31,6 @@ func (e *Enum) valueTag() tag { return enumValueTag } -func (e *Enum) size() int { - return 9 + len(e.name) -} - func (e *Enum) addValue(v interface{}) { e.values = append(e.values, v.(int32)) } @@ -41,3 +38,11 @@ func (e *Enum) addValue(v interface{}) { func (e *Enum) marshal() []byte { return marshalInteger(enumValueTag, e.name, e.values) } + +func (e *Enum) unmarshal(byteStream *bufio.Reader) { + soi, err := unmarshalIntegers(byteStream, integerValueTag) + if err != nil { + return + } + e.values = soi.values +} diff --git a/packages/ipp/integer.go b/packages/ipp/integer.go index 5cb00bb..b278d15 100644 --- a/packages/ipp/integer.go +++ b/packages/ipp/integer.go @@ -3,10 +3,10 @@ package ipp import ( + "bufio" "bytes" "encoding/binary" "fmt" - "io" ) type Integer struct { @@ -33,10 +33,6 @@ func (i *Integer) valueTag() tag { return integerValueTag } -func (i *Integer) size() int { - return 9 + len(i.name) // The attribute tag + 2 lengths -} - func (i *Integer) addValue(v interface{}) { i.values = append(i.values, v.(int32)) } @@ -45,6 +41,15 @@ func (i *Integer) marshal() []byte { return marshalInteger(integerValueTag, i.name, i.values) } +func (i *Integer) unmarshal(byteStream *bufio.Reader) { + soi, err := unmarshalIntegers(byteStream, integerValueTag) + if err != nil { + return + } + i.name = soi.name + i.values = soi.values +} + func marshalInteger(t tag, name string, values []int32) []byte { l := 9 + len(name) + 9*(len(values)-1) b := make([]byte, 0, l) @@ -62,8 +67,3 @@ func marshalInteger(t tag, name string, values []int32) []byte { } return buf.Bytes() } - -func unmarshalSingleInteger(byteStream io.Reader) (string, int32) { - name, data := unmarshalSingleAttribute(byteStream) - return name, int32(binary.BigEndian.Uint32(data)) -} diff --git a/packages/ipp/integer_test.go b/packages/ipp/integer_test.go index 4112d1c..5c831ff 100644 --- a/packages/ipp/integer_test.go +++ b/packages/ipp/integer_test.go @@ -3,6 +3,7 @@ package ipp import ( + "bufio" "bytes" "testing" @@ -18,10 +19,12 @@ func TestUnMarshalSinglePositiveInteger(T *testing.T) { } buf := bytes.NewBuffer(testdata) + bufbuf := bufio.NewReader(buf) - n, v := unmarshalSingleInteger(buf) - assert.Equal(T, "flop", n, "Should be equal") - assert.Equal(T, int32(4), v, "Should be equal") + var i Integer + i.unmarshal(bufbuf) + assert.Equal(T, "flop", i.name, "Should be equal") + assert.Equal(T, int32(4), i.values[0], "Should be equal") } func TestUnMarshalSingleNegativeInteger(T *testing.T) { @@ -31,10 +34,11 @@ func TestUnMarshalSingleNegativeInteger(T *testing.T) { 0x00, 0x04, 0xff, 0xff, 0xff, 0xfc, } - buf := bytes.NewBuffer(testdata) + bufbuf := bufio.NewReader(buf) - n, v := unmarshalSingleInteger(buf) - assert.Equal(T, "flop", n, "Should be equal") - assert.Equal(T, int32(-4), v, "Should be equal") + var i Integer + i.unmarshal(bufbuf) + assert.Equal(T, "flop", i.name, "Should be equal") + assert.Equal(T, int32(-4), i.values[0], "Should be equal") } diff --git a/packages/ipp/keyword.go b/packages/ipp/keyword.go index 3277d3f..4968acd 100644 --- a/packages/ipp/keyword.go +++ b/packages/ipp/keyword.go @@ -2,6 +2,10 @@ // SPDX-License-Identifier: BSD-3-Clause package ipp +import ( + "bufio" +) + type KeyWord struct { sos *SetOfStrings } @@ -21,10 +25,6 @@ func (k KeyWord) String() string { return k.sos.String() } -func (k *KeyWord) size() int { - return k.sos.size() -} - func (k *KeyWord) valueTag() tag { return k.sos.valueTag() } @@ -33,7 +33,10 @@ func (k *KeyWord) marshal() []byte { return k.sos.marshal() } +func (k *KeyWord) unmarshal(byteStream *bufio.Reader) { + k.sos.unmarshal(byteStream, keyWordValueTag) +} + func (k *KeyWord) addValue(v interface{}) { k.sos.AddValue(v.(string)) } - diff --git a/packages/ipp/keyword_test.go b/packages/ipp/keyword_test.go index 248ad0c..5fa1631 100644 --- a/packages/ipp/keyword_test.go +++ b/packages/ipp/keyword_test.go @@ -3,6 +3,8 @@ package ipp import ( + "bufio" + "bytes" "testing" "github.com/stretchr/testify/assert" @@ -22,6 +24,28 @@ import ( // assert.Equal(T, k.values[0], "printer-make-and-model") // } +func TestUnmarshalKeywordWithMultipleStrings(t *testing.T) { + // test data from rfc8010 example A8 Get-Jobs request + testdata := []byte{0x00, 0x14, + 'r', 'e', 'q', 'u', 'e', 's', 't', 'e', 'd', '-', 'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', 's', + 0x00, 0x06, + 'j', 'o', 'b', '-', 'i', 'd', + 0x44, 0x00, 0x00, 0x00, 0x08, + 'j', 'o', 'b', '-', 'n', 'a', 'm', 'e', + 0x44, 0x00, 0x00, 0x00, 0x0f, + 'd', 'o', 'c', 'u', 'm', 'e', 'n', 't', '-', 'f', 'o', 'r', 'm', 'a', 't', + } + buf := bytes.NewBuffer(testdata) + r := bufio.NewReader(buf) + k := NewKeyWord("") + k.unmarshal(r) + assert.Equal(t, k.Name(), "requested-attributes") + assert.Len(t, k.sos.values, 3) + assert.Equal(t, k.sos.values[0], "job-id") + assert.Equal(t, k.sos.values[1], "job-name") + assert.Equal(t, k.sos.values[2], "document-format") +} + func TestMarshalSimpleKeyword(T *testing.T) { testdata := []byte{ 0x44, 0x00, 0x14, @@ -36,3 +60,28 @@ func TestMarshalSimpleKeyword(T *testing.T) { assert.Equal(T, testdata, m, "Should be equal") } + +func TestUnMarshalKeywordWithAdditionalValues(T *testing.T) { + testdata := []byte{ + 0x00, 0x04, + 0x72, 0x65, 0x71, 0x75, + 0x00, 0x06, + 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, + 0x44, 0x00, 0x00, + 0x00, 0x04, + 0x6e, 0x69, 0x72, 0x70, + 0x44, 0x00, 0x00, + 0x00, 0x05, + 0x70, 0x72, 0x69, 0x6e, 0x74, 0x00, + } + + b := bytes.NewBuffer(testdata) + buf := bufio.NewReader(b) + k := NewKeyWord("") + k.unmarshal(buf) + + assert.Equal(T, "requ", k.sos.name, "Wrong name") + assert.Equal(T, "printe", k.sos.values[0], "Wrong value 0") + assert.Equal(T, "nirp", k.sos.values[1], "Wrong value 1") + assert.Equal(T, "print", k.sos.values[2], "Wrong value 2") +} diff --git a/packages/ipp/messages.go b/packages/ipp/messages.go index 5fe5b44..67133bd 100644 --- a/packages/ipp/messages.go +++ b/packages/ipp/messages.go @@ -97,18 +97,18 @@ const ( Stopped printerState = 5 ) -// jobstate defined in rfc8011 ch 5.3.7 -type jobState uint16 +// // jobstate defined in rfc8011 ch 5.3.7 +// type jobState uint16 -const ( - pending jobState = 3 - pendingHeld jobState = 4 - processing jobState = 5 - processingStoppped jobState = 6 - cancelled jobState = 7 - aborted jobState = 8 - completed jobState = 9 -) +// const ( +// pending jobState = 3 +// pendingHeld jobState = 4 +// processing jobState = 5 +// processingStoppped jobState = 6 +// cancelled jobState = 7 +// aborted jobState = 8 +// completed jobState = 9 +// ) type statusCode uint16 @@ -128,19 +128,8 @@ func (v versionNumber) String() string { return fmt.Sprintf("%x.%x", vn&0xff00>>8, vn&0x00ff) } -func unmarshalSingleAttribute(byteStream io.Reader) (string, []byte) { - var length uint16 - binary.Read(byteStream, binary.BigEndian, &length) - attributeName := make([]byte, length) - if length > 0 { - binary.Read(byteStream, binary.BigEndian, attributeName) - } - binary.Read(byteStream, binary.BigEndian, &length) - attributeValue := make([]byte, length) - binary.Read(byteStream, binary.BigEndian, attributeValue) - return string(attributeName), attributeValue -} - +// unmarshalSingleValue reads a single key value pair from the byte stream +// RFC 8010: Attribute-with-one-value func unmarshalSingleValue(byteStream io.Reader) (string, string) { var length uint16 binary.Read(byteStream, binary.BigEndian, &length) @@ -165,11 +154,52 @@ func marshalNameValue(name, value string, b []byte) { copy(b[p:], []byte(value)) } +// func readAttribute(byteStream *bufio.Reader) (string, [][]byte) { +// var length uint16 +// values := make([][]byte, 0, 1) +// binary.Read(byteStream, binary.BigEndian, &length) +// attributeName := make([]byte, length) +// if length > 0 { +// binary.Read(byteStream, binary.BigEndian, attributeName) +// } + + +// binary.Read(byteStream, binary.BigEndian, attributeValue) +// values = append(values, string(attributeValue)) +// next, err := byteStream.Peek(1) +// if err != nil { +// panic("Failed to peek") +// } +// if next[0] != byte(keyWordValueTag) { +// // No additional values +// return string(attributeName), values +// } +// binary.Read(byteStream, binary.BigEndian, &length) +// for length > 0 { +// attributeValue := make([]byte, length) + +// binary.Read(byteStream, binary.BigEndian, &length) +// attributeValue := make([]byte, length) +// if length > 0 { +// binary.Read(byteStream, binary.BigEndian, attributeValue) +// } +// values = append(values, string(attributeValue)) +// next, err := byteStream.Peek(1) +// if err != nil { +// panic("Failed to peek") +// } +// if next[0] != byte(keyWordValueTag) { +// // No additional values +// return string(attributeName), values +// } +// } + +// } + type Attribute interface { Name() string valueTag() tag marshal() []byte - //size() int } type Attributes struct { @@ -230,11 +260,10 @@ func UnMarshalAttributes(bytestream *bufio.Reader) *Attributes { } currentAttributeGroup := t - var lastAddValuer AddValuer for { err = binary.Read(bytestream, binary.BigEndian, &t) if err != nil { - log.Fatal("End of input before end of attributes tag (%v)", err.Error()) + log.Fatalf("End of input before end of attributes tag (%v)", err.Error()) } log.Debugf("Value tag - %v", t) switch t { @@ -260,15 +289,10 @@ func UnMarshalAttributes(bytestream *bufio.Reader) *Attributes { a.addAttribute(currentAttributeGroup, n) log.Debugf("%v %v", n.name, n.value) case keyWordValueTag: - name, value := unmarshalSingleValue(bytestream) - if name == "" { - lastAddValuer.addValue(value) - } else { - k := NewKeyWord(name, value) - a.addAttribute(currentAttributeGroup, k) - lastAddValuer = k - } - log.Debugf("%v : %v", name, value) + k := NewKeyWord("", "") + k.unmarshal(bytestream) + a.addAttribute(currentAttributeGroup, k) + log.Debugf("%v : %v", k.Name(), k.String()) case nameWithoutLanguageValueTag: n := NewNameWithoutLanguage("", "") n.unmarshal(bytestream) @@ -280,45 +304,25 @@ func UnMarshalAttributes(bytestream *bufio.Reader) *Attributes { a.addAttribute(currentAttributeGroup, attr) log.Debugf("%v %v", attr.name, attr.value) case mimeMediaTypeValueTag: - name, value := unmarshalSingleValue(bytestream) - if name == "" { - lastAddValuer.addValue(value) - } else { - m := NewMimeMediaType(name, value) - a.addAttribute(currentAttributeGroup, m) - lastAddValuer = m - } - log.Debugf("%v : %v", name, value) + m := NewMimeMediaType("") + m.unmarshal(bytestream) + a.addAttribute(currentAttributeGroup, m) + log.Debugf("%v : %v", m.Name(), m.String()) case integerValueTag: - name, value := unmarshalSingleInteger(bytestream) - if name == "" { - lastAddValuer.addValue(value) - } else { - i := NewInteger(name, value) - a.addAttribute(currentAttributeGroup, i) - lastAddValuer = i - } - log.Debugf("%v : %v", name, value) + i := NewInteger("") + i.unmarshal(bytestream) + a.addAttribute(currentAttributeGroup, i) + log.Debugf("%v : %v", i.name, i.values) case rangeOfIntegerValueTag: - name, value := unmarshalSingleRangeOfInteger(bytestream) - if name == "" { - lastAddValuer.addValue(value) - } else { - r := NewRangeOfInteger(name, value) - a.addAttribute(currentAttributeGroup, r) - lastAddValuer = r - } - log.Debugf("%v : %v", name, value) + r := NewRangeOfInteger("") + r.unmarshal(bytestream) + a.addAttribute(currentAttributeGroup, r) + log.Debugf("%v : %v", r.name, r.values) case enumValueTag: - name, value := unmarshalSingleInteger(bytestream) - if name == "" { - lastAddValuer.addValue(value) - } else { - e := NewEnum(name, value) - a.addAttribute(currentAttributeGroup, e) - lastAddValuer = e - } - log.Debugf("%v : %v", name, value) + e := NewEnum("") + e.unmarshal(bytestream) + a.addAttribute(currentAttributeGroup, e) + log.Debugf("%v : %v", e.name, e.values) case begCollectionValueTag: // For now just consume the collection consumeCollection(bytestream) @@ -326,7 +330,6 @@ func UnMarshalAttributes(bytestream *bufio.Reader) *Attributes { attr := NewUnsupportedValue() attr.unmarshal(bytestream) a.addAttribute(currentAttributeGroup, attr) - case jobAttributes: log.Debug("Start job attributes") currentAttributeGroup = jobAttributes diff --git a/packages/ipp/mimemediatype.go b/packages/ipp/mimemediatype.go index 8c6a1fd..338f7c9 100644 --- a/packages/ipp/mimemediatype.go +++ b/packages/ipp/mimemediatype.go @@ -2,6 +2,8 @@ // SPDX-License-Identifier: BSD-3-Clause package ipp +import "bufio" + type MimeMediaType struct { sos *SetOfStrings } @@ -21,10 +23,6 @@ func (m MimeMediaType) String() string { return m.sos.String() } -func (m *MimeMediaType) size() int { - return m.sos.size() -} - func (m *MimeMediaType) valueTag() tag { return m.sos.valueTag() } @@ -33,6 +31,10 @@ func (m *MimeMediaType) marshal() []byte { return m.sos.marshal() } +func (m *MimeMediaType) unmarshal(byteStream *bufio.Reader) { + m.sos.unmarshal(byteStream, mimeMediaTypeValueTag) +} + func (m *MimeMediaType) addValue(v interface{}) { m.sos.AddValue(v.(string)) } diff --git a/packages/ipp/namewithoutlanguage.go b/packages/ipp/namewithoutlanguage.go index b1a6ebb..f9c7b1d 100644 --- a/packages/ipp/namewithoutlanguage.go +++ b/packages/ipp/namewithoutlanguage.go @@ -39,13 +39,6 @@ func (n *NameWithoutLanguage) marshal() []byte { return b } -func (n *NameWithoutLanguage) size() int { - l := 1 + 4 // The attribute tag + 2 lengths - l += len(n.name) - l += len(n.value) - return l -} - func (n NameWithoutLanguage) Value() string { return n.value } diff --git a/packages/ipp/naturallanguage.go b/packages/ipp/naturallanguage.go index f3e3237..0adbd11 100644 --- a/packages/ipp/naturallanguage.go +++ b/packages/ipp/naturallanguage.go @@ -41,10 +41,3 @@ func (c *NaturalLanguage) marshal() []byte { marshalNameValue(c.name, c.value, b[1:]) return b } - -func (c *NaturalLanguage) size() int { - l := 1 + 4 // The attribute tag + 2 lengths - l += len(c.name) - l += len(c.value) - return l -} diff --git a/packages/ipp/rangeofinteger.go b/packages/ipp/rangeofinteger.go index a5d6f61..ab7f1ee 100644 --- a/packages/ipp/rangeofinteger.go +++ b/packages/ipp/rangeofinteger.go @@ -3,9 +3,9 @@ package ipp import ( + "bufio" "encoding/binary" "fmt" - "io" log "github.com/sirupsen/logrus" ) @@ -44,14 +44,44 @@ func (r *RangeOfInteger) marshal() []byte { return []byte{} } +func (r *RangeOfInteger) unmarshal(byteStream *bufio.Reader) error { + var length uint16 + r.values = make([]IRange, 0, 1) + binary.Read(byteStream, binary.BigEndian, &length) + name := make([]byte, length) + if length > 0 { + binary.Read(byteStream, binary.BigEndian, name) + } + r.name = string(name) + binary.Read(byteStream, binary.BigEndian, &length) + if length != 8 { + return fmt.Errorf("wrong value-length of range-of-integer attribute %v", length) + } + for length > 0 { + var i IRange + err := binary.Read(byteStream, binary.BigEndian, &i.lower) + if err != nil { + return err + } + err = binary.Read(byteStream, binary.BigEndian, &i.upper) + if err != nil { + return err + } + r.values = append(r.values, i) + next, err := byteStream.Peek(3) + if err != nil { + break + } + if next[0] != byte(rangeOfIntegerValueTag) || next[1] != 0x00 || next[2] != 0x00 { + break + } + // Remove the value tag with the zero length from the stream + byteStream.Discard(3) + binary.Read(byteStream, binary.BigEndian, &length) + } + return nil +} + func (r *RangeOfInteger) addValue(v interface{}) { r.values = append(r.values, v.(IRange)) } - -func unmarshalSingleRangeOfInteger(byteStream io.Reader) (string, IRange) { - name, data := unmarshalSingleAttribute(byteStream) - var r IRange - r.lower = int32(binary.BigEndian.Uint32(data[0:4])) - r.upper = int32(binary.BigEndian.Uint32(data[4:8])) - return name, r -} diff --git a/packages/ipp/rangeofinteger_test.go b/packages/ipp/rangeofinteger_test.go index 56e2920..2218771 100644 --- a/packages/ipp/rangeofinteger_test.go +++ b/packages/ipp/rangeofinteger_test.go @@ -3,24 +3,51 @@ package ipp import ( + "bufio" "bytes" "testing" "github.com/stretchr/testify/assert" ) -func TestUnMarshalSingleRange(T *testing.T) { +func TestUnmarshalSingleRange(T *testing.T) { testdata := []byte{ 0x00, 0x04, 0x66, 0x6c, 0x6f, 0x70, //flop 0x00, 0x08, 0x00, 0x0, 0x0, 0x4, 0x00, 0x0, 0x0, 0x5, } + b := bytes.NewBuffer(testdata) + buf := bufio.NewReader(b) - buf := bytes.NewBuffer(testdata) + var r RangeOfInteger + r.unmarshal(buf) + + assert.Equal(T, "flop", r.name, "Should be equal") + assert.Equal(T, int32(4), r.values[0].lower, "Should be equal") + assert.Equal(T, int32(5), r.values[0].upper, "Should be equal") +} + +func TestUnmarshalDualRanges(T *testing.T) { + testdata := []byte{ + 0x00, 0x04, + 0x66, 0x6c, 0x6f, 0x70, //flop + 0x00, 0x08, + 0x00, 0x0, 0x0, 0x4, 0x00, 0x0, 0x0, 0x5, + 0x33, 0x00, 0x00, + 0x00, 0x08, + 0x00, 0x0, 0x0, 0x6, 0x00, 0x0, 0x0, 0x9, + } + b := bytes.NewBuffer(testdata) + buf := bufio.NewReader(b) + + var r RangeOfInteger + r.unmarshal(buf) + + assert.Equal(T, "flop", r.name, "Should be equal") + assert.Equal(T, int32(4), r.values[0].lower, "Should be equal") + assert.Equal(T, int32(5), r.values[0].upper, "Should be equal") + assert.Equal(T, int32(6), r.values[1].lower, "Should be equal") + assert.Equal(T, int32(9), r.values[1].upper, "Should be equal") - n, v := unmarshalSingleRangeOfInteger(buf) - assert.Equal(T, "flop", n, "Should be equal") - assert.Equal(T, int32(4), v.lower, "Should be equal") - assert.Equal(T, int32(5), v.upper, "Should be equal") } diff --git a/packages/ipp/setofintegers.go b/packages/ipp/setofintegers.go new file mode 100644 index 0000000..43fc1f5 --- /dev/null +++ b/packages/ipp/setofintegers.go @@ -0,0 +1,56 @@ +// Copyright 2021, Henrik Sölver henrik.solver@gmail.com +// SPDX-License-Identifier: BSD-3-Clause +package ipp + +import ( + "bufio" + "encoding/binary" + "fmt" +) + +// setOfIntegers is a helper type that is used to handle integer types +// which only differs in tag value. +type setOfIntegers struct { + name string + values []int32 +} + +func unmarshalIntegers(byteStream *bufio.Reader, valueTag tag) (*setOfIntegers, error) { + + s := new(setOfIntegers) + s.values = make([]int32, 0, 1) + + var length uint16 + binary.Read(byteStream, binary.BigEndian, &length) + name := make([]byte, length) + if length > 0 { + binary.Read(byteStream, binary.BigEndian, name) + } + s.name = string(name) + binary.Read(byteStream, binary.BigEndian, &length) + if length != 4 { + return nil, fmt.Errorf("wrong value-length of integer attribute %v", length) + } + var value int32 //valueBytes := make([]byte, length) + for length > 0 { + err := binary.Read(byteStream, binary.BigEndian, &value) + if err != nil { + return nil, err + } + s.values = append(s.values, value) + next, err := byteStream.Peek(3) + if err != nil { + // end of byte stream + break + } + if next[0] != byte(valueTag) || next[1] != 0x00 || next[2] != 0x00 { + // End of attribute + break + } + // Remove the value tag with the zero length from the stream + byteStream.Discard(3) + binary.Read(byteStream, binary.BigEndian, &length) + } + + return s, nil +} diff --git a/packages/ipp/setofstrings.go b/packages/ipp/setofstrings.go index 08a6a11..f5ad641 100644 --- a/packages/ipp/setofstrings.go +++ b/packages/ipp/setofstrings.go @@ -2,7 +2,10 @@ // SPDX-License-Identifier: BSD-3-Clause package ipp -import "encoding/binary" +import ( + "bufio" + "encoding/binary" +) // SetOfStrings is the strings attribute type SetOfStrings struct { @@ -32,17 +35,37 @@ func (s *SetOfStrings) valueTag() tag { return s.vTag } -// func (k *keyWord) unmarshal(byteStream io.Reader) { -// if len(k.values) == 0 { -// var v string -// k.name, v = unmarshalSingleValue(byteStream) -// k.values = append(k.values, v) -// } else { -// var v string -// _, v = unmarshalSingleValue(byteStream) -// k.values = append(k.values, v) -// } -// } +func (s *SetOfStrings) unmarshal(byteStream *bufio.Reader, valueTag tag) error { + var length uint16 + s.vTag = valueTag + s.values = make([]string, 0, 1) + binary.Read(byteStream, binary.BigEndian, &length) + name := make([]byte, length) + if length > 0 { + binary.Read(byteStream, binary.BigEndian, name) + } + s.name = string(name) + binary.Read(byteStream, binary.BigEndian, &length) + for length > 0 { + valueBytes := make([]byte, length) + err := binary.Read(byteStream, binary.BigEndian, valueBytes) + if err != nil { + return err + } + s.values = append(s.values, string(valueBytes)) + next, err := byteStream.Peek(3) + if err != nil { + break + } + if next[0] != byte(valueTag) || next[1] != 0x00 || next[2] != 0x00 { + break + } + // Remove the value tag with the zero length from the stream + byteStream.Discard(3) + binary.Read(byteStream, binary.BigEndian, &length) + } + return nil +} func (s *SetOfStrings) marshal() []byte { l := 5 + len(s.name) + len(s.values[0]) @@ -79,16 +102,3 @@ func (s *SetOfStrings) marshal() []byte { func (s *SetOfStrings) AddValue(v string) { s.values = append(s.values, v) } - -func (s *SetOfStrings) size() int { - l := 1 + 2 // The value tag (0x44) + name-length field (2 bytes) - l += len(s.name) - l += 2 // value-length field (2 bytes) - l += len(s.values[0]) - // Add all additional values - for _, v := range s.values[1:] { - l += 1 + 4 // The value tag (0x44) + 2 length fields (2 bytes) - l += len(v) - } - return l -} diff --git a/packages/ipp/textwithoutlanguage.go b/packages/ipp/textwithoutlanguage.go index a8a2a5c..015a785 100644 --- a/packages/ipp/textwithoutlanguage.go +++ b/packages/ipp/textwithoutlanguage.go @@ -42,10 +42,3 @@ func (c *TextWithoutLanguage) marshal() []byte { marshalNameValue(c.name, c.value, b[1:]) return b } - -func (c *TextWithoutLanguage) size() int { - l := 1 + 4 // The attribute tag + 2 lengths - l += len(c.name) - l += len(c.value) - return l -}