diff --git a/codec/h264/h264dec/cabac.go b/codec/h264/h264dec/cabac.go index b5940a00..7b28414e 100644 --- a/codec/h264/h264dec/cabac.go +++ b/codec/h264/h264dec/cabac.go @@ -28,8 +28,6 @@ LICENSE package h264dec import ( - "math" - "bitbucket.org/ausocean/av/codec/h264/h264dec/bits" "github.com/pkg/errors" ) @@ -306,80 +304,6 @@ var ( } ) -// Errors used by mbTypeBinarization. -var ( - errBadMbType = errors.New("macroblock type outside of valid range") - errBadMbSliceType = errors.New("bad slice type for macroblock") -) - -// mbTypeBinarization returns the macroblock type binarization for the given -// macroblock type value and slice type using the process defined in section -// 9.3.2.5 of the specifications. -func mbTypeBinarization(v, slice int) ([]int, error) { - switch slice { - case sliceTypeI: - if v < minIMbType || v > maxIMbType { - return nil, errBadMbType - } - return binOfIMBTypes[v], nil - - case sliceTypeSI: - if v < minSIMbType || v > maxSIMbType { - return nil, errBadMbType - } - if v == sliceTypeSI { - return []int{0}, nil - } - return append([]int{1}, binOfIMBTypes[v-1]...), nil - - case sliceTypeP, sliceTypeSP: - if v < minPOrSPMbType || v > maxPOrSPMbType || v == P8x8ref0 { - return nil, errBadMbType - } - if v < 5 { - return binOfPOrSPMBTypes[v], nil - } - return append([]int{1}, binOfIMBTypes[v-5]...), nil - - case sliceTypeB: - if v < minBMbType || v > maxBMbType { - return nil, errBadMbType - } - if v < 23 { - return binOfBMBTypes[v], nil - } - return append([]int{1, 1, 1, 1, 0, 1}, binOfIMBTypes[v-23]...), nil - - default: - return nil, errBadMbSliceType - } -} - -// Error used by subMbTypeBinarization. -var errBadSubMbSliceType = errors.New("bad slice type for sub-macroblock") - -// subMbTypeBinarization returns the binarization of a sub-macroblock type -// given the slice in which it is in using the process defined in section -// 9.3.2.5 of the specifications. -func subMbTypeBinarization(v, slice int) ([]int, error) { - switch slice { - case sliceTypeP, sliceTypeSP: - if v < minPOrSPSubMbType || v > maxPOrSPSubMbType { - return nil, errBadMbType - } - return binOfPOrSPSubMBTypes[v], nil - - case sliceTypeB: - if v < minBSubMbType || v > maxBSubMbType { - return nil, errBadMbType - } - return binOfBSubMBTypes[v], nil - - default: - return nil, errBadSubMbSliceType - } -} - // Table 9-34 type MaxBinIdxCtx struct { // When false, Prefix is the MaxBinIdxCtx @@ -762,119 +686,3 @@ func CtxIdx(binIdx, maxBinIdxCtx, ctxIdxOffset int) int { return ctxIdx } - -// Error used by unaryBinarization. -var errNegativeSyntaxVal = errors.New("cannot get unary binarization of negative value") - -// unaryBinarization returns the unary binarization of a syntax element having -// value v, as specified by setion 9.3.2.1 in the specifications. -func unaryBinarization(v int) ([]int, error) { - if v < 0 { - return nil, errNegativeSyntaxVal - } - r := make([]int, v+1) - for i := 0; i <= v; i++ { - if i < v { - r[i] = 1 - } - } - return r, nil -} - -// Error used by truncUnaryBinarization. -var errInvalidSyntaxVal = errors.New("syntax value cannot be greater than cMax") - -// truncUnaryBinarization returns the truncated unary binarization of a syntax -// element v given a cMax as specified in section 9.3.2.2 of the specifications. -func truncUnaryBinarization(v, cMax int) ([]int, error) { - if v < 0 { - return nil, errNegativeSyntaxVal - } - - if v > cMax { - return nil, errInvalidSyntaxVal - } - - if v == cMax { - b, _ := unaryBinarization(v) - return b[:len(b)-1], nil - } - return unaryBinarization(v) -} - -// Error used by unaryExpGolombBinarization. -var errInvalidUCoff = errors.New("uCoff cannot be less than or equal to zero") - -// unaryExpGolombBinarization returns the concatendated unary/k-th order -// Exp-Golomb (UEGk) binarization of a syntax element using the process defined -// in section 9.3.2.3 of the specifications. -func unaryExpGolombBinarization(v, uCoff, k int, signedValFlag bool) ([]int, error) { - if uCoff <= 0 { - return nil, errInvalidUCoff - } - - prefix, err := truncUnaryBinarization(mini(uCoff, absi(v)), uCoff) - if err != nil { - return nil, err - } - - return append(prefix, suffix(v, uCoff, k, signedValFlag)...), nil -} - -// suffix returns the suffix part of a unary k-th Exp-Golomb Binarization -// using the the algorithm as described by pseudo code 9-6 in section 9.3.2.3. -// TODO: could probably reduce allocations. -func suffix(v, uCoff, k int, signedValFlag bool) []int { - var s []int - - if absi(v) >= uCoff { - sufS := absi(v) - uCoff - var stop bool - - for { - if sufS >= (1 << uint(k)) { - s = append(s, 1) - sufS = sufS - (1 << uint(k)) - k++ - } else { - s = append(s, 0) - for k = k - 1; k >= 0; k-- { - s = append(s, (sufS>>uint(k))&1) - } - stop = true - } - if stop { - break - } - } - } - - if signedValFlag && v != 0 { - if v > 0 { - s = append(s, 0) - } else { - s = append(s, 1) - } - } - - return s -} - -// Error used by fixedLenBinariztion. -var errNegativeValue = errors.New("cannot get fixed length binarization of negative value") - -// fixedLenBinarization returns the fixed-length (FL) binarization of the syntax -// element v, given cMax to determine bin length, as specified by section 9.3.2.4 -// of the specifications. -func fixedLenBinarization(v, cMax int) ([]int, error) { - if v < 0 { - return nil, errNegativeValue - } - l := int(math.Ceil(math.Log2(float64(cMax + 1)))) - r := make([]int, l) - for i := l - 1; i >= 0; i-- { - r[i] = v % 2 - v = v / 2 - } - return r, nil -} diff --git a/codec/h264/h264dec/cabac_test.go b/codec/h264/h264dec/cabac_test.go index 5ca17ce8..20f1e066 100644 --- a/codec/h264/h264dec/cabac_test.go +++ b/codec/h264/h264dec/cabac_test.go @@ -26,74 +26,9 @@ LICENSE package h264dec import ( - "reflect" "testing" ) -func TestMbTypeBinarization(t *testing.T) { - tests := []struct { - v, slice int - want []int - err error - }{ - {v: 6, slice: sliceTypeI, want: []int{1, 0, 0, 1, 0, 0, 1}}, - {v: 26, slice: sliceTypeI, err: errBadMbType}, - {v: -1, slice: sliceTypeI, err: errBadMbType}, - {v: 4, slice: sliceTypeSI, want: []int{0}}, - {v: 6, slice: sliceTypeSI, want: []int{1, 1, 0, 0, 1, 0, 0, 0}}, - {v: 0, slice: sliceTypeSI, err: errBadMbType}, - {v: 27, slice: sliceTypeSI, err: errBadMbType}, - {v: 2, slice: sliceTypeP, want: []int{0, 1, 0}}, - {v: 3, slice: sliceTypeSP, want: []int{0, 0, 1}}, - {v: 7, slice: sliceTypeP, want: []int{1, 1, 0, 0, 0, 0, 1}}, - {v: 7, slice: sliceTypeSP, want: []int{1, 1, 0, 0, 0, 0, 1}}, - {v: -1, slice: sliceTypeP, err: errBadMbType}, - {v: 31, slice: sliceTypeP, err: errBadMbType}, - {v: 8, slice: sliceTypeB, want: []int{1, 1, 0, 1, 0, 1}}, - {v: 30, slice: sliceTypeB, want: []int{1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0}}, - {v: -1, slice: sliceTypeB, err: errBadMbType}, - {v: 49, slice: sliceTypeB, err: errBadMbType}, - {v: 6, slice: 20, err: errBadMbSliceType}, - } - - for i, test := range tests { - got, err := mbTypeBinarization(test.v, test.slice) - if err != test.err { - t.Errorf("did not get expected error for test %d\nGot: %v\nWant: %v", i, err, test.err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("did not get expected result for test %d\nGot: %v\nWant: %v", i, got, test.want) - } - } -} - -func TestSubMbTypeBinarization(t *testing.T) { - tests := []struct { - v, slice int - want []int - err error - }{ - {v: 2, slice: sliceTypeP, want: []int{0, 1, 1}}, - {v: 2, slice: sliceTypeSP, want: []int{0, 1, 1}}, - {v: -1, slice: sliceTypeSP, err: errBadMbType}, - {v: 4, slice: sliceTypeSP, err: errBadMbType}, - {v: 9, slice: sliceTypeB, want: []int{1, 1, 1, 0, 1, 0}}, - {v: 9, slice: 40, err: errBadSubMbSliceType}, - } - - for i, test := range tests { - got, err := subMbTypeBinarization(test.v, test.slice) - if err != test.err { - t.Errorf("did not get expected error for test %d\nGot: %v\nWant: %v", i, err, test.err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("did not get expected result for test %d\nGot: %v\nWant: %v", i, got, test.want) - } - } -} - var ctxIdxTests = []struct { binIdx int maxBinIdxCtx int @@ -211,144 +146,3 @@ func TestCtxIdx(t *testing.T) { } } } - -func TestUnaryBinarization(t *testing.T) { - // Test data has been extracted from table 9-35 of the specifications. - tests := []struct { - in int - want []int - err error - }{ - {in: 0, want: []int{0}, err: nil}, - {in: 1, want: []int{1, 0}, err: nil}, - {in: 2, want: []int{1, 1, 0}, err: nil}, - {in: 3, want: []int{1, 1, 1, 0}, err: nil}, - {in: 4, want: []int{1, 1, 1, 1, 0}, err: nil}, - {in: 5, want: []int{1, 1, 1, 1, 1, 0}, err: nil}, - {in: -3, want: nil, err: errNegativeSyntaxVal}, - } - - for i, test := range tests { - got, err := unaryBinarization(test.in) - if err != test.err { - t.Errorf("did not get expected error for test %d\nGot: %v\nWant: %v", i, err, test.err) - } - - if !reflect.DeepEqual(test.want, got) { - t.Errorf("did not get expected result for test %d\nGot: %v\nWant: %v\n", i, got, test.want) - } - } -} - -func TestFixedLengthBinarization(t *testing.T) { - tests := []struct { - v int - cMax int - want []int - err error - }{ - {v: 0, cMax: 7, want: []int{0, 0, 0}}, - {v: 1, cMax: 7, want: []int{0, 0, 1}}, - {v: 2, cMax: 7, want: []int{0, 1, 0}}, - {v: 3, cMax: 7, want: []int{0, 1, 1}}, - {v: 4, cMax: 7, want: []int{1, 0, 0}}, - {v: 5, cMax: 7, want: []int{1, 0, 1}}, - {v: 6, cMax: 7, want: []int{1, 1, 0}}, - {v: 7, cMax: 7, want: []int{1, 1, 1}}, - {v: -1, cMax: 7, want: nil, err: errNegativeValue}, - } - - for i, test := range tests { - got, err := fixedLenBinarization(test.v, test.cMax) - if err != test.err { - t.Errorf("did not get expected error for test %d\nGot: %v\nWant: %v\n", i, err, test.err) - } - - if !reflect.DeepEqual(test.want, got) { - t.Errorf("did not get expected result for test %d\nGot: %v\nWant: %v\n", i, got, test.want) - } - } -} - -func TestTruncUnaryBinarization(t *testing.T) { - tests := []struct { - v int - cMax int - want []int - err error - }{ - {v: 0, cMax: 10, want: []int{0}, err: nil}, - {v: 1, cMax: 10, want: []int{1, 0}, err: nil}, - {v: 2, cMax: 10, want: []int{1, 1, 0}, err: nil}, - {v: 0, cMax: 0, want: []int{}, err: nil}, - {v: 4, cMax: 4, want: []int{1, 1, 1, 1}, err: nil}, - {v: 1, cMax: 10, want: []int{1, 0}, err: nil}, - {v: 2, cMax: 10, want: []int{1, 1, 0}, err: nil}, - {v: -3, cMax: 10, want: nil, err: errNegativeSyntaxVal}, - {v: 5, cMax: 4, want: nil, err: errInvalidSyntaxVal}, - } - - for i, test := range tests { - got, err := truncUnaryBinarization(test.v, test.cMax) - if err != test.err { - t.Errorf("did not get expected error for test %d\nGot: %v\nWant: %v", i, err, test.err) - } - - if !reflect.DeepEqual(test.want, got) { - t.Errorf("did not get expected result for test %d\nGot: %v\nWant: %v\n", i, got, test.want) - } - } -} - -func TestUEGkSuffix(t *testing.T) { - // Data from https://patents.google.com/patent/US20070092150 - tests := []struct { - v, uCoff, k int - signedValFlag bool - want []int - }{ - 0: {v: 14, uCoff: 14, want: []int{0}}, - 1: {v: 15, uCoff: 14, want: []int{1, 0, 0}}, - 2: {v: 16, uCoff: 14, want: []int{1, 0, 1}}, - 3: {v: 17, uCoff: 14, want: []int{1, 1, 0, 0, 0}}, - 4: {v: 18, uCoff: 14, want: []int{1, 1, 0, 0, 1}}, - 5: {v: 19, uCoff: 14, want: []int{1, 1, 0, 1, 0}}, - 6: {v: 20, uCoff: 14, want: []int{1, 1, 0, 1, 1}}, - 7: {v: 21, uCoff: 14, want: []int{1, 1, 1, 0, 0, 0, 0}}, - 8: {v: 22, uCoff: 14, want: []int{1, 1, 1, 0, 0, 0, 1}}, - 9: {v: 23, uCoff: 14, want: []int{1, 1, 1, 0, 0, 1, 0}}, - 10: {v: 24, uCoff: 14, want: []int{1, 1, 1, 0, 0, 1, 1}}, - 11: {v: 25, uCoff: 14, want: []int{1, 1, 1, 0, 1, 0, 0}}, - } - - for i, test := range tests { - got := suffix(test.v, test.uCoff, test.k, test.signedValFlag) - if !reflect.DeepEqual(got, test.want) { - t.Errorf("did not get expected result for test %d\nGot: %v\nWant: %v\n", i, got, test.want) - } - } -} - -func TestUnaryExpGolombBinarization(t *testing.T) { - tests := []struct { - v, uCoff, k int - signedValFlag bool - want []int - }{ - 0: {v: 7, uCoff: 14, want: []int{1, 1, 1, 1, 1, 1, 1, 0}}, - 1: {v: 17, uCoff: 14, want: []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}}, - 2: {v: 15, uCoff: 14, signedValFlag: true, want: []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}}, - 3: {v: -15, uCoff: 14, signedValFlag: true, want: []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1}}, - } - - for i, test := range tests { - got, err := unaryExpGolombBinarization(test.v, test.uCoff, test.k, test.signedValFlag) - if err != nil { - t.Errorf("did not expect error %v for test %d", err, i) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("did not get expected result for test %d\nGot: %v\nWant: %v\n", i, got, test.want) - } - } -} diff --git a/codec/h264/h264dec/cabacenc.go b/codec/h264/h264dec/cabacenc.go new file mode 100644 index 00000000..224fc529 --- /dev/null +++ b/codec/h264/h264dec/cabacenc.go @@ -0,0 +1,222 @@ +/* +TODO: this file should really be in a 'h264enc' package. + +DESCRIPTION + cabacenc.go provides functionality for CABAC encoding. + +AUTHORS + Saxon A. Nelson-Milton + +LICENSE + Copyright (C) 2019 the Australian Ocean Lab (AusOcean). + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + in gpl.txt. If not, see http://www.gnu.org/licenses. +*/ + +package h264dec + +import ( + "errors" + "math" +) + +// Error used by unaryBinString. +var errNegativeSyntaxVal = errors.New("cannot get unary binary string of negative value") + +// unaryBinString returns the unary binary string of a syntax element having +// value v, as specified by setion 9.3.2.1 in the specifications. +func unaryBinString(v int) ([]int, error) { + if v < 0 { + return nil, errNegativeSyntaxVal + } + r := make([]int, v+1) + for i := 0; i <= v; i++ { + if i < v { + r[i] = 1 + } + } + return r, nil +} + +// Error used by truncUnaryBinString. +var errInvalidSyntaxVal = errors.New("syntax value cannot be greater than cMax") + +// truncUnaryBinString returns the truncated unary binary string of a syntax +// element v given a cMax as specified in section 9.3.2.2 of the specifications. +func truncUnaryBinString(v, cMax int) ([]int, error) { + if v < 0 { + return nil, errNegativeSyntaxVal + } + + if v > cMax { + return nil, errInvalidSyntaxVal + } + + if v == cMax { + b, _ := unaryBinString(v) + return b[:len(b)-1], nil + } + return unaryBinString(v) +} + +// Error used by unaryExpGolombBinString. +var errInvalidUCoff = errors.New("uCoff cannot be less than or equal to zero") + +// unaryExpGolombBinString returns the concatendated unary/k-th order +// Exp-Golomb (UEGk) binary string of a syntax element using the process defined +// in section 9.3.2.3 of the specifications. +func unaryExpGolombBinString(v, uCoff, k int, signedValFlag bool) ([]int, error) { + if uCoff <= 0 { + return nil, errInvalidUCoff + } + + prefix, err := truncUnaryBinString(mini(uCoff, absi(v)), uCoff) + if err != nil { + return nil, err + } + + return append(prefix, suffix(v, uCoff, k, signedValFlag)...), nil +} + +// suffix returns the suffix part of a unary k-th Exp-Golomb binar string +// using the the algorithm as described by pseudo code 9-6 in section 9.3.2.3. +// TODO: could probably reduce allocations. +func suffix(v, uCoff, k int, signedValFlag bool) []int { + var s []int + + if absi(v) >= uCoff { + sufS := absi(v) - uCoff + var stop bool + + for { + if sufS >= (1 << uint(k)) { + s = append(s, 1) + sufS = sufS - (1 << uint(k)) + k++ + } else { + s = append(s, 0) + for k = k - 1; k >= 0; k-- { + s = append(s, (sufS>>uint(k))&1) + } + stop = true + } + if stop { + break + } + } + } + + if signedValFlag && v != 0 { + if v > 0 { + s = append(s, 0) + } else { + s = append(s, 1) + } + } + + return s +} + +// Error used by fixedLenBinString. +var errNegativeValue = errors.New("cannot get fixed length binary string of negative value") + +// fixedLenBinString returns the fixed-length (FL) binary string of the syntax +// element v, given cMax to determine bin length, as specified by section 9.3.2.4 +// of the specifications. +func fixedLenBinString(v, cMax int) ([]int, error) { + if v < 0 { + return nil, errNegativeValue + } + l := int(math.Ceil(math.Log2(float64(cMax + 1)))) + r := make([]int, l) + for i := l - 1; i >= 0; i-- { + r[i] = v % 2 + v = v / 2 + } + return r, nil +} + +// Errors used by mbTypeBinString. +var ( + errBadMbType = errors.New("macroblock type outside of valid range") + errBadMbSliceType = errors.New("bad slice type for macroblock") +) + +// mbTypeBinString returns the macroblock type binary string for the given +// macroblock type value and slice type using the process defined in section +// 9.3.2.5 of the specifications. +func mbTypeBinString(v, slice int) ([]int, error) { + switch slice { + case sliceTypeI: + if v < minIMbType || v > maxIMbType { + return nil, errBadMbType + } + return binOfIMBTypes[v], nil + + case sliceTypeSI: + if v < minSIMbType || v > maxSIMbType { + return nil, errBadMbType + } + if v == sliceTypeSI { + return []int{0}, nil + } + return append([]int{1}, binOfIMBTypes[v-1]...), nil + + case sliceTypeP, sliceTypeSP: + if v < minPOrSPMbType || v > maxPOrSPMbType || v == P8x8ref0 { + return nil, errBadMbType + } + if v < 5 { + return binOfPOrSPMBTypes[v], nil + } + return append([]int{1}, binOfIMBTypes[v-5]...), nil + + case sliceTypeB: + if v < minBMbType || v > maxBMbType { + return nil, errBadMbType + } + if v < 23 { + return binOfBMBTypes[v], nil + } + return append([]int{1, 1, 1, 1, 0, 1}, binOfIMBTypes[v-23]...), nil + + default: + return nil, errBadMbSliceType + } +} + +// Error used by subMbTypeBinString. +var errBadSubMbSliceType = errors.New("bad slice type for sub-macroblock") + +// subMbTypeBinString returns the binary string of a sub-macroblock type +// given the slice in which it is in using the process defined in section +// 9.3.2.5 of the specifications. +func subMbTypeBinString(v, slice int) ([]int, error) { + switch slice { + case sliceTypeP, sliceTypeSP: + if v < minPOrSPSubMbType || v > maxPOrSPSubMbType { + return nil, errBadMbType + } + return binOfPOrSPSubMBTypes[v], nil + + case sliceTypeB: + if v < minBSubMbType || v > maxBSubMbType { + return nil, errBadMbType + } + return binOfBSubMBTypes[v], nil + + default: + return nil, errBadSubMbSliceType + } +} diff --git a/codec/h264/h264dec/cabacenc_test.go b/codec/h264/h264dec/cabacenc_test.go new file mode 100644 index 00000000..d27b1624 --- /dev/null +++ b/codec/h264/h264dec/cabacenc_test.go @@ -0,0 +1,237 @@ +/* +TODO: this file should really be in a 'h264enc' package. + +DESCRIPTION + cabacenc_test.go provides testing for functionality found in cabacenc.go. + +AUTHORS + Saxon A. Nelson-Milton + +LICENSE + Copyright (C) 2019 the Australian Ocean Lab (AusOcean). + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + in gpl.txt. If not, see http://www.gnu.org/licenses. +*/ + +package h264dec + +import ( + "reflect" + "testing" +) + +func TestMbTypeBinString(t *testing.T) { + tests := []struct { + v, slice int + want []int + err error + }{ + {v: 6, slice: sliceTypeI, want: []int{1, 0, 0, 1, 0, 0, 1}}, + {v: 26, slice: sliceTypeI, err: errBadMbType}, + {v: -1, slice: sliceTypeI, err: errBadMbType}, + {v: 4, slice: sliceTypeSI, want: []int{0}}, + {v: 6, slice: sliceTypeSI, want: []int{1, 1, 0, 0, 1, 0, 0, 0}}, + {v: 0, slice: sliceTypeSI, err: errBadMbType}, + {v: 27, slice: sliceTypeSI, err: errBadMbType}, + {v: 2, slice: sliceTypeP, want: []int{0, 1, 0}}, + {v: 3, slice: sliceTypeSP, want: []int{0, 0, 1}}, + {v: 7, slice: sliceTypeP, want: []int{1, 1, 0, 0, 0, 0, 1}}, + {v: 7, slice: sliceTypeSP, want: []int{1, 1, 0, 0, 0, 0, 1}}, + {v: -1, slice: sliceTypeP, err: errBadMbType}, + {v: 31, slice: sliceTypeP, err: errBadMbType}, + {v: 8, slice: sliceTypeB, want: []int{1, 1, 0, 1, 0, 1}}, + {v: 30, slice: sliceTypeB, want: []int{1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0}}, + {v: -1, slice: sliceTypeB, err: errBadMbType}, + {v: 49, slice: sliceTypeB, err: errBadMbType}, + {v: 6, slice: 20, err: errBadMbSliceType}, + } + + for i, test := range tests { + got, err := mbTypeBinString(test.v, test.slice) + if err != test.err { + t.Errorf("did not get expected error for test %d\nGot: %v\nWant: %v", i, err, test.err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("did not get expected result for test %d\nGot: %v\nWant: %v", i, got, test.want) + } + } +} + +func TestSubMbTypeBinString(t *testing.T) { + tests := []struct { + v, slice int + want []int + err error + }{ + {v: 2, slice: sliceTypeP, want: []int{0, 1, 1}}, + {v: 2, slice: sliceTypeSP, want: []int{0, 1, 1}}, + {v: -1, slice: sliceTypeSP, err: errBadMbType}, + {v: 4, slice: sliceTypeSP, err: errBadMbType}, + {v: 9, slice: sliceTypeB, want: []int{1, 1, 1, 0, 1, 0}}, + {v: 9, slice: 40, err: errBadSubMbSliceType}, + } + + for i, test := range tests { + got, err := subMbTypeBinString(test.v, test.slice) + if err != test.err { + t.Errorf("did not get expected error for test %d\nGot: %v\nWant: %v", i, err, test.err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("did not get expected result for test %d\nGot: %v\nWant: %v", i, got, test.want) + } + } +} + +func TestUnaryBinString(t *testing.T) { + // Test data has been extracted from table 9-35 of the specifications. + tests := []struct { + in int + want []int + err error + }{ + {in: 0, want: []int{0}, err: nil}, + {in: 1, want: []int{1, 0}, err: nil}, + {in: 2, want: []int{1, 1, 0}, err: nil}, + {in: 3, want: []int{1, 1, 1, 0}, err: nil}, + {in: 4, want: []int{1, 1, 1, 1, 0}, err: nil}, + {in: 5, want: []int{1, 1, 1, 1, 1, 0}, err: nil}, + {in: -3, want: nil, err: errNegativeSyntaxVal}, + } + + for i, test := range tests { + got, err := unaryBinString(test.in) + if err != test.err { + t.Errorf("did not get expected error for test %d\nGot: %v\nWant: %v", i, err, test.err) + } + + if !reflect.DeepEqual(test.want, got) { + t.Errorf("did not get expected result for test %d\nGot: %v\nWant: %v\n", i, got, test.want) + } + } +} + +func TestFixedLengthBinString(t *testing.T) { + tests := []struct { + v int + cMax int + want []int + err error + }{ + {v: 0, cMax: 7, want: []int{0, 0, 0}}, + {v: 1, cMax: 7, want: []int{0, 0, 1}}, + {v: 2, cMax: 7, want: []int{0, 1, 0}}, + {v: 3, cMax: 7, want: []int{0, 1, 1}}, + {v: 4, cMax: 7, want: []int{1, 0, 0}}, + {v: 5, cMax: 7, want: []int{1, 0, 1}}, + {v: 6, cMax: 7, want: []int{1, 1, 0}}, + {v: 7, cMax: 7, want: []int{1, 1, 1}}, + {v: -1, cMax: 7, want: nil, err: errNegativeValue}, + } + + for i, test := range tests { + got, err := fixedLenBinString(test.v, test.cMax) + if err != test.err { + t.Errorf("did not get expected error for test %d\nGot: %v\nWant: %v\n", i, err, test.err) + } + + if !reflect.DeepEqual(test.want, got) { + t.Errorf("did not get expected result for test %d\nGot: %v\nWant: %v\n", i, got, test.want) + } + } +} + +func TestTruncUnaryBinString(t *testing.T) { + tests := []struct { + v int + cMax int + want []int + err error + }{ + {v: 0, cMax: 10, want: []int{0}, err: nil}, + {v: 1, cMax: 10, want: []int{1, 0}, err: nil}, + {v: 2, cMax: 10, want: []int{1, 1, 0}, err: nil}, + {v: 0, cMax: 0, want: []int{}, err: nil}, + {v: 4, cMax: 4, want: []int{1, 1, 1, 1}, err: nil}, + {v: 1, cMax: 10, want: []int{1, 0}, err: nil}, + {v: 2, cMax: 10, want: []int{1, 1, 0}, err: nil}, + {v: -3, cMax: 10, want: nil, err: errNegativeSyntaxVal}, + {v: 5, cMax: 4, want: nil, err: errInvalidSyntaxVal}, + } + + for i, test := range tests { + got, err := truncUnaryBinString(test.v, test.cMax) + if err != test.err { + t.Errorf("did not get expected error for test %d\nGot: %v\nWant: %v", i, err, test.err) + } + + if !reflect.DeepEqual(test.want, got) { + t.Errorf("did not get expected result for test %d\nGot: %v\nWant: %v\n", i, got, test.want) + } + } +} + +func TestUEGkSuffix(t *testing.T) { + // Data from https://patents.google.com/patent/US20070092150 + tests := []struct { + v, uCoff, k int + signedValFlag bool + want []int + }{ + 0: {v: 14, uCoff: 14, want: []int{0}}, + 1: {v: 15, uCoff: 14, want: []int{1, 0, 0}}, + 2: {v: 16, uCoff: 14, want: []int{1, 0, 1}}, + 3: {v: 17, uCoff: 14, want: []int{1, 1, 0, 0, 0}}, + 4: {v: 18, uCoff: 14, want: []int{1, 1, 0, 0, 1}}, + 5: {v: 19, uCoff: 14, want: []int{1, 1, 0, 1, 0}}, + 6: {v: 20, uCoff: 14, want: []int{1, 1, 0, 1, 1}}, + 7: {v: 21, uCoff: 14, want: []int{1, 1, 1, 0, 0, 0, 0}}, + 8: {v: 22, uCoff: 14, want: []int{1, 1, 1, 0, 0, 0, 1}}, + 9: {v: 23, uCoff: 14, want: []int{1, 1, 1, 0, 0, 1, 0}}, + 10: {v: 24, uCoff: 14, want: []int{1, 1, 1, 0, 0, 1, 1}}, + 11: {v: 25, uCoff: 14, want: []int{1, 1, 1, 0, 1, 0, 0}}, + } + + for i, test := range tests { + got := suffix(test.v, test.uCoff, test.k, test.signedValFlag) + if !reflect.DeepEqual(got, test.want) { + t.Errorf("did not get expected result for test %d\nGot: %v\nWant: %v\n", i, got, test.want) + } + } +} + +func TestUnaryExpGolombBinString(t *testing.T) { + tests := []struct { + v, uCoff, k int + signedValFlag bool + want []int + }{ + 0: {v: 7, uCoff: 14, want: []int{1, 1, 1, 1, 1, 1, 1, 0}}, + 1: {v: 17, uCoff: 14, want: []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}}, + 2: {v: 15, uCoff: 14, signedValFlag: true, want: []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}}, + 3: {v: -15, uCoff: 14, signedValFlag: true, want: []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1}}, + } + + for i, test := range tests { + got, err := unaryExpGolombBinString(test.v, test.uCoff, test.k, test.signedValFlag) + if err != nil { + t.Errorf("did not expect error %v for test %d", err, i) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("did not get expected result for test %d\nGot: %v\nWant: %v\n", i, got, test.want) + } + } +}