Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ developed to be used in [`dtn7-go`][dtn7-go], an implementation of the

- Supports a selected subset of [CBOR's][cbor] features:
- Unsigned Integer
- Negative Integer
- Floating-point values
- Byte and Text String
- Arrays, both of definite and indefinite length
Expand Down
1 change: 1 addition & 0 deletions major.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type MajorType = byte

const (
UInt MajorType = 0x00
NInt MajorType = 0x20
ByteString MajorType = 0x40
TextString MajorType = 0x60
Array MajorType = 0x80
Expand Down
52 changes: 51 additions & 1 deletion primitives.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package cboring

import "io"
import (
"fmt"
"io"
)

/*** Uint ***/

Expand All @@ -14,6 +17,53 @@ func WriteUInt(n uint64, w io.Writer) error {
return WriteMajors(UInt, n, w)
}

/*** Nint ***/

// ReadNInt expects a negative integer at the Reader's position and returns -N - 1, with N the actual number.
// when read into int64 N = int64(^n) check if value is negative
func ReadNInt(r io.Reader) (n uint64, err error) {
return ReadExpectMajors(NInt, r)
}

// WriteNInt serializes a negative integer into the Writer. n has to be -N - 1, with N the actual number.
// when used on int64 use n = uint64(^N)
func WriteNInt(n uint64, w io.Writer) error {
return WriteMajors(NInt, n, w)
}

/*** int ***/

// ReadInt expects either an unsigned or negative integer at the Reader's position and returns it if the value fits int64.
func ReadInt(r io.Reader) (n int64, err error) {
major, num, err := ReadMajors(r)
n = int64(num)
if n < 0 {
if major == UInt {
err = fmt.Errorf("ReadInt: Returned Integer %d to big for int64", num)
} else if major == NInt {
err = fmt.Errorf("ReadInt: Returned Integer -%d to small for int64",
num+1) // might overflow but highly unlikely
} else {
err = fmt.Errorf("ReadInt: Wrong Major Type: 0x%x instead of 0x00 or 0x20",
major)
}
} else if major == NInt {
n = ^n
} else if major != UInt {
err = fmt.Errorf("ReadInt: Wrong Major Type: 0x%x instead of 0x00 or 0x20",
major)
}
return
}

// WriteInt serializes an integer into the Writer, either as UInt or NInt.
func WriteInt(n int64, w io.Writer) error {
if n < 0 {
return WriteNInt(uint64(^n), w)
}
return WriteUInt(uint64(n), w)
}

/*** ByteString ***/

// ReadByteStringLen expects a byte string at the Reader's position and returns
Expand Down
136 changes: 136 additions & 0 deletions primitives_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,142 @@ func TestReadUIntError(t *testing.T) {
}
}

/*** NInt ***/

func TestNInt(t *testing.T) {
tests := []struct {
data []byte
numb int64
}{
{[]byte{0x20}, -1},
{[]byte{0x29}, -10},
{[]byte{0x36}, -23},
{[]byte{0x37}, -24},
{[]byte{0x38, 0x18}, -25},
{[]byte{0x38, 0x63}, -100},
{[]byte{0x39, 0x03, 0xe7}, -1000},
{[]byte{0x3a, 0x00, 0x0f, 0x42, 0x3F}, -1000000},
{[]byte{0x3b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x0F, 0xFF}, -1000000000000},
{[]byte{0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0}, /* can not represent -2^64
but underflow would lead to 0, so this is the best representation */
}

for _, test := range tests {
// Read
buff := bytes.NewBuffer(test.data)
if n, err := ReadNInt(buff); err != nil {
t.Fatal(err)
} else if int64(^n) != test.numb {
t.Fatalf("Resulting negative int %d is not %d", int64(^n), test.numb)
}

// Write
buff.Reset()
if err := WriteNInt(uint64(^test.numb), buff); err != nil {
t.Fatal(err)
}

if bb := buff.Bytes(); !reflect.DeepEqual(bb, test.data) {
t.Fatalf("Serialized data mismatches: %x != %x", bb, test.data)
}
}
}

func TestReadNIntError(t *testing.T) {
tests := [][]byte{
// Wrong major type
{0xFF},
// Wrong additionals for major type 1
{NInt | 0x1F},
// Empty stream
{},
// Incomplete streams
{NInt | 0x18}, {0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
}

for _, test := range tests {
r := bytes.NewBuffer(test)
if _, err := ReadNInt(r); err == nil {
t.Fatalf("Illegal input %x did not errored", test)
}
}
}

/*** Int ***/

func TestInt(t *testing.T) {
tests := []struct {
data []byte
numb int64
}{
{[]byte{0x00}, 0},
{[]byte{0x01}, 1},
{[]byte{0x0a}, 10},
{[]byte{0x17}, 23},
{[]byte{0x18, 0x18}, 24},
{[]byte{0x18, 0x19}, 25},
{[]byte{0x18, 0x64}, 100},
{[]byte{0x19, 0x03, 0xe8}, 1000},
{[]byte{0x1a, 0x00, 0x0f, 0x42, 0x40}, 1000000},
{[]byte{0x1b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00}, 1000000000000},
{[]byte{0x20}, -1},
{[]byte{0x29}, -10},
{[]byte{0x36}, -23},
{[]byte{0x37}, -24},
{[]byte{0x38, 0x18}, -25},
{[]byte{0x38, 0x63}, -100},
{[]byte{0x39, 0x03, 0xe7}, -1000},
{[]byte{0x3a, 0x00, 0x0f, 0x42, 0x3F}, -1000000},
{[]byte{0x3b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x0F, 0xFF}, -1000000000000},
}

for _, test := range tests {
// Read
buff := bytes.NewBuffer(test.data)
if n, err := ReadInt(buff); err != nil {
t.Fatal(err)
} else if n != test.numb {
t.Fatalf("Resulting int %d is not %d", n, test.numb)
}

// Write
buff.Reset()
if err := WriteInt(test.numb, buff); err != nil {
t.Fatal(err)
}

if bb := buff.Bytes(); !reflect.DeepEqual(bb, test.data) {
t.Fatalf("Serialized data mismatches: %x != %x", bb, test.data)
}
}
}

func TestReadIntError(t *testing.T) {
tests := [][]byte{
// Wrong major type
{0xFF},
// Wrong additionals for major type 0
{0x1F},
// Wrong additionals for major type 1
{NInt | 0x1F},
// Empty stream
{},
// Incomplete streams
{0x18}, {0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
// Number to big
{0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
// Number to small
{NInt | 0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
}

for _, test := range tests {
r := bytes.NewBuffer(test)
if _, err := ReadInt(r); err == nil {
t.Fatalf("Illegal input %x did not errored", test)
}
}
}

/*** ByteString ***/

func TestByteStringLen(t *testing.T) {
Expand Down
Loading