Skip to content
This repository has been archived by the owner on Jul 12, 2022. It is now read-only.

Commit

Permalink
[#7] support little/big endian operations on a byte array
Browse files Browse the repository at this point in the history
- Create BigEndianOperations accessor with
  - Mask function,
  - InclusiveMerge function,
  - ExclusiveMerge function,
  - ExtractBytes function.

- Create LittleEndianOperations accessor with
  - Mask function,
  - InclusiveMerge function,
  - ExclusiveMerge function,
  - ExtractBytes function.

- [#31] Fix LeftShift and RightShift functions.

- Add some Endianess-free operations
  - Add Mask function on two arrays of the same size,
  - Add InclusiveMerge function on two arrays of the same size,
  - Add ExclusiveMerge function on two arrays of the same size,
  - Add LeftPad function,
  - Add RightPad function.

Closes #7, #31
  • Loading branch information
rlespinasse committed Oct 28, 2017
1 parent 92c23b7 commit c25839e
Show file tree
Hide file tree
Showing 6 changed files with 638 additions and 218 deletions.
76 changes: 76 additions & 0 deletions big_endian_operations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package byteness

type bigEndianOperations struct{}

// BigEndianOperations test
var BigEndianOperations = &bigEndianOperations{}

// Mask apply AND mask to byte array
func (op *bigEndianOperations) Mask(data, mask []byte) []byte {
var dataLength = len(data)
if dataLength < 1 {
return data
}

var maskLength = len(mask)
var operationLength = dataLength
var operationCut = dataLength
if maskLength > dataLength {
operationLength = maskLength
}

result, _ := Mask(rightPad(data, operationLength, 0xFF), rightPad(mask, operationLength, 0xFF))
return result[:operationCut]
}

// InclusiveMerge apply OR operation on two byte arrays
func (op *bigEndianOperations) InclusiveMerge(data1, data2 []byte) []byte {
var data1Length = len(data1)
var data2Length = len(data2)

var operationLength = data1Length
if data2Length > data1Length {
operationLength = data2Length
}

result, _ := InclusiveMerge(rightPad(data1, operationLength, 0x00), rightPad(data2, operationLength, 0x00))
return result
}

// ExclusiveMerge apply XOR operation on two byte arrays
func (op *bigEndianOperations) ExclusiveMerge(data1, data2 []byte) []byte {
var data1Length = len(data1)
var data2Length = len(data2)

var operationLength = data1Length
if data2Length > data1Length {
operationLength = data2Length
}

result, _ := ExclusiveMerge(rightPad(data1, operationLength, 0x00), rightPad(data2, operationLength, 0x00))
return result
}

// ExtractBytes get a byte array as subset of another byte array
func (op *bigEndianOperations) ExtractBytes(data []byte, lsbPosition, msbPosition uint64) []byte {
var maxMsb = uint64(byteLength*len(data) - 1)

if msbPosition <= lsbPosition || lsbPosition > maxMsb {
return make([]byte, 0)
}

if msbPosition > maxMsb {
msbPosition = maxMsb
}

var result = RightShift(data, maxMsb-msbPosition)
var correctiveShift = maxMsb - msbPosition + lsbPosition
result = LeftShift(result, correctiveShift)

var size = computeSize(lsbPosition, msbPosition)
return op.trim(result, size)
}

func (op *bigEndianOperations) trim(data []byte, newSize uint64) []byte {
return data[:newSize]
}
169 changes: 169 additions & 0 deletions big_endian_operations_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package byteness

import (
"reflect"
"testing"
)

var testcasesBigEndianOperationsMask = []struct {
name string
data []byte
mask []byte
result []byte
}{
{"data and mask of equal length", []byte{0xDA, 0x99, 0xBA}, []byte{0xAD, 0x11, 0xAB}, []byte{0x88, 0x11, 0xAA}},
{"data shorter than mask", []byte{0xDA, 0x99, 0xBA}, []byte{0xAD, 0x11, 0xAB, 0x88, 0x11, 0xAA}, []byte{0x88, 0x11, 0xAA}},
{"data longer than mask", []byte{0xAD, 0x11, 0xAB, 0x88, 0x11, 0xAA}, []byte{0xDA, 0x99, 0xBA}, []byte{0x88, 0x11, 0xAA, 0x88, 0x11, 0xAA}},
{"empty mask on data", []byte{0xDA, 0x99, 0xBA}, []byte{}, []byte{0xDA, 0x99, 0xBA}},
{"mask on empty data", []byte{}, []byte{0xAD, 0x11, 0xAB}, []byte{}},
{"empty mask on empty data", []byte{}, []byte{}, []byte{}},
}

func TestBigEndianOperationsMask(t *testing.T) {
var val []byte
for _, tc := range testcasesBigEndianOperationsMask {
t.Run(tc.name, func(t *testing.T) {
val = BigEndianOperations.Mask(tc.data, tc.mask)
if !reflect.DeepEqual(val, tc.result) {
t.Errorf("BigEndianOperations.Mask(%x, %x) was %x, should be %x",
tc.data, tc.mask,
val,
tc.result)
}
})
}
}

func BenchmarkBigEndianOperationsMask(b *testing.B) {
var val []byte
for _, tc := range testcasesBigEndianOperationsMask {
b.Run(tc.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
val = BigEndianOperations.Mask(tc.data, tc.mask)
}
})
}
}

var testcasesBigEndianOperationsInclusiveMerge = []struct {
name string
data1 []byte
data2 []byte
result []byte
}{
{"equal length arrays", []byte{0xDA, 0x99, 0xBA}, []byte{0xAD, 0x11, 0xAB}, []byte{0xFF, 0x99, 0xBB}},
{"first array longer", []byte{0xAD, 0x11, 0xAB, 0xFF, 0x99, 0xBB}, []byte{0xDA, 0x99, 0xBA}, []byte{0xFF, 0x99, 0xBB, 0xFF, 0x99, 0xBB}},
{"second array longer", []byte{0xDA, 0x99, 0xBA}, []byte{0xAD, 0x11, 0xAB, 0xFF, 0x99, 0xBB}, []byte{0xFF, 0x99, 0xBB, 0xFF, 0x99, 0xBB}},
{"first array empty", []byte{}, []byte{0xAD, 0x11, 0xAB}, []byte{0xAD, 0x11, 0xAB}},
{"second array empty", []byte{0xDA, 0x99, 0xBA}, []byte{}, []byte{0xDA, 0x99, 0xBA}},
{"empty arrays", []byte{}, []byte{}, []byte{}},
}

func TestBigEndianOperationsInclusiveMerge(t *testing.T) {
var val []byte
for _, tc := range testcasesBigEndianOperationsInclusiveMerge {
t.Run(tc.name, func(t *testing.T) {
val = BigEndianOperations.InclusiveMerge(tc.data1, tc.data2)
if !reflect.DeepEqual(val, tc.result) {
t.Errorf("BigEndianOperations.InclusiveMerge(%x, %x) was %x, should be %x",
tc.data1, tc.data2,
val,
tc.result)
}
})
}
}

func BenchmarkBigEndianOperationsInclusiveMerge(b *testing.B) {
var val []byte
for _, tc := range testcasesBigEndianOperationsInclusiveMerge {
b.Run(tc.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
val = BigEndianOperations.InclusiveMerge(tc.data1, tc.data2)
}
})
}
}

var testcasesBigEndianOperationsExclusiveMerge = []struct {
name string
data1 []byte
data2 []byte
result []byte
}{
{"equal length arrays", []byte{0xDA, 0x99, 0xBA}, []byte{0xAD, 0x11, 0xAB}, []byte{0x77, 0x88, 0x11}},
{"first array longer", []byte{0xAD, 0x11, 0xAB, 0x77, 0x88, 0x11}, []byte{0xDA, 0x99, 0xBA}, []byte{0x77, 0x88, 0x11, 0x77, 0x88, 0x11}},
{"second array longer", []byte{0xDA, 0x99, 0xBA}, []byte{0xAD, 0x11, 0xAB, 0x77, 0x88, 0x11}, []byte{0x77, 0x88, 0x11, 0x77, 0x88, 0x11}},
{"first array empty", []byte{}, []byte{0xAD, 0x11, 0xAB}, []byte{0xAD, 0x11, 0xAB}},
{"second array empty", []byte{0xDA, 0x99, 0xBA}, []byte{}, []byte{0xDA, 0x99, 0xBA}},
{"empty arrays", []byte{}, []byte{}, []byte{}},
}

func TestBigEndianOperationsExclusiveMerge(t *testing.T) {
var val []byte
for _, tc := range testcasesBigEndianOperationsExclusiveMerge {
t.Run(tc.name, func(t *testing.T) {
val = BigEndianOperations.ExclusiveMerge(tc.data1, tc.data2)
if !reflect.DeepEqual(val, tc.result) {
t.Errorf("BigEndianOperations.ExclusiveMerge(%x, %x) was %x, should be %x",
tc.data1, tc.data2,
val,
tc.result)
}
})
}
}

func BenchmarkBigEndianOperationsExclusiveMerge(b *testing.B) {
var val []byte
for _, tc := range testcasesBigEndianOperationsExclusiveMerge {
b.Run(tc.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
val = BigEndianOperations.ExclusiveMerge(tc.data1, tc.data2)
}
})
}
}

var testcasesBigEndianOperationsExtractBytes = []struct {
name string
data []byte
lsbPosition, msbPosition uint64
result []byte
}{
{"extract nothing", []byte{0xDA, 0x99, 0xBA}, 0, 0, []byte{}},
{"extract nothing due to inversed positions", []byte{0xDA, 0x99, 0xBA}, 16, 8, []byte{}},
{"extract nothing due to wrong positions", []byte{0xDA, 0x99, 0xBA}, 100, 101, []byte{}},
{"extract only in one byte", []byte{0xDA, 0x99, 0xBA}, 5, 7, []byte{0x40}},
{"extract one byte over two bytes", []byte{0xDA, 0x99, 0xBA}, 7, 8, []byte{0x40}},
{"extract two bytes over three bytes", []byte{0xDA, 0x99, 0xBA}, 6, 17, []byte{0xA6, 0x60}},
{"extract three bytes over three bytes", []byte{0xDA, 0x99, 0xBA}, 1, 22, []byte{0xB5, 0x33, 0x74}},
{"extract all bytes", []byte{0xDA, 0x99, 0xBA}, 0, 23, []byte{0xDA, 0x99, 0xBA}},
{"extract all bytes with an overflow position", []byte{0xDA, 0x99, 0xBA}, 0, 100, []byte{0xDA, 0x99, 0xBA}},
}

func TestBigEndianOperationsExtractBytes(t *testing.T) {
var val []byte
for _, tc := range testcasesBigEndianOperationsExtractBytes {
t.Run(tc.name, func(t *testing.T) {
val = BigEndianOperations.ExtractBytes(tc.data, tc.lsbPosition, tc.msbPosition)
if !reflect.DeepEqual(val, tc.result) {
t.Errorf("BigEndianOperations.ExtractBytes(%x, %v, %v) was %x, should be %x",
tc.data, tc.lsbPosition, tc.msbPosition,
val,
tc.result)
}
})
}
}

func BenchmarkBigEndianOperationsExtractBytes(b *testing.B) {
var val []byte
for _, tc := range testcasesBigEndianOperationsExtractBytes {
b.Run(tc.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
val = BigEndianOperations.ExtractBytes(tc.data, tc.lsbPosition, tc.msbPosition)
}
})
}
}
Loading

0 comments on commit c25839e

Please sign in to comment.