diff --git a/base.go b/base.go index b8383bd..e8322bc 100644 --- a/base.go +++ b/base.go @@ -2,6 +2,7 @@ package unibase2n import ( "errors" + "math/bits" ) // Base has an encoding buffer thus should not be copied. @@ -46,11 +47,14 @@ func (bs Base) check() error { if offe > 0x10000 { return ErrOffsetOverflow } + if bits.OnesCount8(bit) == 1 { // 2的幂永无offset, 无需管til (1/2/4/8) + return nil + } tile := uint32(til) // [til, tile) - if bit > 8 && bit%2 == 0 { - tile += uint32(bit / 2) + if bit > 4 && bit%2 == 0 { + tile += uint32(bit / 2) // 6 10 12 14 } else { - tile += uint32(bit) + tile += uint32(bit) // 3 5 7 9 11 13 15 } if tile > 0x10000 { return ErrTailOverflow diff --git a/decode.go b/decode.go index 1051725..58e2023 100644 --- a/decode.go +++ b/decode.go @@ -2,14 +2,58 @@ package unibase2n import ( "encoding/binary" + "math/bits" ) -func (bs Base) Decode(data []byte) []byte { - return nil +func (bs Base) Decode(data []byte) (out []byte) { + if len(data) == 0 || len(data)%2 != 0 { + return nil + } + if bits.OnesCount8(bs.bit) == 1 { // 2的幂永无offset, 无需管til (1/2/4/8) + out = make([]byte, bs.DecodeLen(len(data), 0)) + switch bs.bit { + case 8: + dec16blk8(bs.off, data, out) + case 4: + mask := uint32(bs.off) | uint32(bs.off)<<16 + dec32blk4(mask, data, out) + case 2: + mask := uint64(bs.off) | uint64(bs.off)<<16 | uint64(bs.off)<<32 | uint64(bs.off)<<48 + dec64blk2(mask, data, out) + case 1: + mask64 := uint64(bs.off) | uint64(bs.off)<<16 | uint64(bs.off)<<32 | uint64(bs.off)<<48 + mask := uint128be{a: mask64, b: mask64} + dec128blk1(mask, data, out) + } + return + } + offset := 0 + tile := uint32(bs.til) // [til, tile) + if bs.bit > 4 && bs.bit%2 == 0 { + tile += uint32(bs.bit / 2) // 6 10 12 14 + } else { + tile += uint32(bs.bit) // 3 5 7 9 11 13 15 + } + realtail := binary.BigEndian.Uint16(data[len(data)-2:]) + if realtail >= bs.til && uint32(realtail) < tile { + offset = int(realtail - bs.til) + } + out = make([]byte, bs.DecodeLen(len(data), offset)) + switch bs.bit { + case 3, 5, 7, 9, 11, 13, 15: + mask64 := uint64(bs.off) | uint64(bs.off)<<16 | uint64(bs.off)<<32 | uint64(bs.off)<<48 + mask := uint128be{a: mask64, b: mask64} + dec128blk(mask, bs.bit, data, out) + case 6, 10, 12, 14: + mask := uint64(bs.off) | uint64(bs.off)<<16 | uint64(bs.off)<<32 | uint64(bs.off)<<48 + dec64blk(mask, bs.bit, data, out) + } + return } // dec128blk1 for bit 1 // len(in)>0, len(in)%16==0, len(out)==len(in)/16 +//go:nosplit func dec128blk1(mask uint128be, in, out []byte) { for i := range out { c := i * 16 @@ -43,21 +87,183 @@ func dec128blk1(mask uint128be, in, out []byte) { } // dec64blk2 for bit 2 -// len(in)!=0, len(out)==len(in)/8 +// len(in)>0, len(in)%8==0, len(out)==len(in)/8 +//go:nosplit func dec64blk2(mask uint64, in, out []byte) { - for i, n := range in { + for i := range out { c := i * 8 - x := (uint64(n)<<42 | uint64(n)<<28 | uint64(n)<<14 | uint64(n)) & 0x00030003_00030003 - binary.BigEndian.PutUint64(out[c:c+8], x+mask) + n := binary.BigEndian.Uint64(in[c:c+8]) - mask + sum := n & 0x03 + n >>= 16 - 2 + sum |= n & 0x0c + n >>= 16 - 2 + sum |= n & 0x30 + n >>= 16 - 2 + sum |= n & 0xc0 + out[i] = uint8(sum) } } // dec32blk4 for bit 4 -// len(in)!=0, len(out)==len(in)/4 +// len(in)>0, len(in)%4==0, len(out)==len(in)/4 +//go:nosplit func dec32blk4(mask uint32, in, out []byte) { - for i, n := range in { + for i := range out { c := i * 4 - x := (uint32(n)<<12 | uint32(n)) & 0x000f000f - binary.BigEndian.PutUint32(out[c:c+4], x+mask) + n := binary.BigEndian.Uint32(in[c:c+4]) - mask + sum := n & 0x0f + n >>= 16 - 4 + sum |= n & 0xf0 + out[i] = uint8(sum) + } +} + +// dec16blk8 for bit 8 +// len(in)>0, len(in)%2==0, len(out)==len(in)/2 +//go:nosplit +func dec16blk8(mask uint16, in, out []byte) { + for i := range out { + c := i * 2 + n := binary.BigEndian.Uint16(in[c:c+2]) - mask + out[i] = uint8(n) + } +} + +// dec128blk for bit 3 5 6 7 9 11 13 15 +// len(in)>0, len(in)%2==0, len(out)==len(in)/16*bit+offset +//go:nosplit +func dec128blk(mask uint128be, bit uint8, in, out []byte) { + var tmp [16]byte + i := 0 + n := 0 + c := 16 - bit + for ; i <= len(out)-int(bit); n += 16 { + m := uint128be{a: (uint64(1)<0, len(in)%2==0, len(out)==len(in)/16*bit+offset +//go:nosplit +func dec64blk(mask uint64, bit uint8, in, out []byte) { + var tmp [8]byte + i := 0 + s := int(bit) >> 1 + n := 0 + c := 16 - bit + for ; i <= len(out)-s; n += 8 { + m := (uint64(1)<>= bit + sum |= shift & m + shift <<= c + m >>= bit + sum |= shift & m + shift <<= c + m >>= bit + sum |= shift & m + if len(out[i:]) < 8 { + binary.BigEndian.PutUint64(tmp[:], sum) + copy(out[i:], tmp[:]) + } else { + binary.BigEndian.PutUint64(out[i:], sum) + } + i += s + } + if n < len(in)-2 { + m := (uint64(1)<>= bit + sum |= shift & m + shift <<= c + m >>= bit + sum |= shift & m + shift <<= c + m >>= bit + sum |= shift & m + binary.BigEndian.PutUint64(tmp[:], sum) + copy(out[i:], tmp[:]) } } diff --git a/decode_test.go b/decode_test.go index 96ffbe5..318ffe5 100644 --- a/decode_test.go +++ b/decode_test.go @@ -1,12 +1,33 @@ package unibase2n import ( + "bytes" + "math/bits" "math/rand" "testing" "github.com/stretchr/testify/assert" ) +func TestEnDecode(t *testing.T) { + var buf [4096]byte + _, err := rand.Read(buf[:]) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, 1, bits.OnesCount8(8)) + bs := Base{off: 0, til: 32768, bit: 1} + for ; bs.bit < 16; bs.bit++ { + for i := 1; i <= 4096; i++ { + e := bs.Encode(buf[:i]) + d := bs.Decode(e) + if !bytes.Equal(d, buf[:i]) { + t.Fatal(bs.bit, i, e, d, buf[:i]) + } + } + } +} + func TestDec128blk1(t *testing.T) { var in, tmp [32]byte _, err := rand.Read(in[:]) @@ -20,3 +41,100 @@ func TestDec128blk1(t *testing.T) { dec128blk1(uint128be{0x2333233323332333, 0x2333233323332333}, out[:], tmp[:]) assert.Equal(t, in, tmp) } + +func TestDec64blk2(t *testing.T) { + var in, tmp [32]byte + _, err := rand.Read(in[:]) + if err != nil { + t.Fatal(err) + } + n, _ := Base{bit: 2}.EncodeLen(32) + out := make([]byte, n) + enc64blk2(0x2333233323332333, in[:], out) + t.Log(out) + dec64blk2(0x2333233323332333, out[:], tmp[:]) + assert.Equal(t, in, tmp) +} + +func TestDec32blk4(t *testing.T) { + var in, tmp [32]byte + _, err := rand.Read(in[:]) + if err != nil { + t.Fatal(err) + } + n, _ := Base{bit: 4}.EncodeLen(32) + out := make([]byte, n) + enc32blk4(0x23332333, in[:], out) + t.Log(out) + dec32blk4(0x23332333, out[:], tmp[:]) + assert.Equal(t, in, tmp) +} + +func TestDec16blk8(t *testing.T) { + var in, tmp [32]byte + _, err := rand.Read(in[:]) + if err != nil { + t.Fatal(err) + } + n, _ := Base{bit: 8}.EncodeLen(32) + out := make([]byte, n) + enc16blk8(0x2333, in[:], out) + t.Log(out) + dec16blk8(0x2333, out[:], tmp[:]) + assert.Equal(t, in, tmp) +} + +func TestDec128blk(t *testing.T) { + var in, tmp [32]byte + _, err := rand.Read(in[:]) + if err != nil { + t.Fatal(err) + } + mask := uint128be{0x2333233323332333, 0x2333233323332333} + bs := Base{} + for _, bit := range [...]byte{3, 5, 6, 7, 9, 11, 13, 15} { + bs.bit = bit + n, _ := bs.EncodeLen(32) + out := make([]byte, n) + enc128blk(mask, bit, in[:], out) + t.Log(out) + dec128blk(mask, bit, out[:], tmp[:]) + assert.Equal(t, in, tmp) + } +} + +func TestDec64blk(t *testing.T) { + var in, tmp [32]byte + _, err := rand.Read(in[:]) + if err != nil { + t.Fatal(err) + } + // 6 + n, _ := Base{bit: 6}.EncodeLen(32) + out := make([]byte, n) + enc64blk(0x2333233323332333, 6, in[:], out) + t.Log(out) + dec64blk(0x2333233323332333, 6, out[:], tmp[:]) + assert.Equal(t, in, tmp) + // 10 + n, _ = Base{bit: 10}.EncodeLen(32) + out = make([]byte, n) + enc64blk(0x2333233323332333, 10, in[:], out) + t.Log(out) + dec64blk(0x2333233323332333, 10, out[:], tmp[:]) + assert.Equal(t, in, tmp) + // 12 + n, _ = Base{bit: 12}.EncodeLen(32) + out = make([]byte, n) + enc64blk(0x2333233323332333, 12, in[:], out) + t.Log(out) + dec64blk(0x2333233323332333, 12, out[:], tmp[:]) + assert.Equal(t, in, tmp) + // 14 + n, _ = Base{bit: 14}.EncodeLen(32) + out = make([]byte, n) + enc64blk(0x2333233323332333, 14, in[:], out) + t.Log(out) + dec64blk(0x2333233323332333, 14, out[:], tmp[:]) + assert.Equal(t, in, tmp) +} diff --git a/encode.go b/encode.go index 48062f5..7042007 100644 --- a/encode.go +++ b/encode.go @@ -38,6 +38,7 @@ func (bs Base) Encode(data []byte) (out []byte) { // enc16blk1 for bit 1 (actual enc128blk1) // len(in)!=0, len(out)==len(in)*16 +//go:nosplit func enc16blk1(mask uint16, in, out []byte) { for i, n := range in { c := i * 16 @@ -54,6 +55,7 @@ func enc16blk1(mask uint16, in, out []byte) { // enc64blk2 for bit 2 // len(in)!=0, len(out)==len(in)*8 +//go:nosplit func enc64blk2(mask uint64, in, out []byte) { for i, n := range in { c := i * 8 @@ -64,6 +66,7 @@ func enc64blk2(mask uint64, in, out []byte) { // enc32blk4 for bit 4 // len(in)!=0, len(out)==len(in)*4 +//go:nosplit func enc32blk4(mask uint32, in, out []byte) { for i, n := range in { c := i * 4 @@ -74,6 +77,7 @@ func enc32blk4(mask uint32, in, out []byte) { // enc16blk8 for bit 8 // len(in)!=0, len(out)==len(in)*2 +//go:nosplit func enc16blk8(mask uint16, in, out []byte) { for i, n := range in { c := i * 2 @@ -83,6 +87,7 @@ func enc16blk8(mask uint16, in, out []byte) { // enc128blk for bit 3 5 6 7 9 11 13 15 // len(in)>0, len(out)==len(in)/bit*16 +//go:nosplit func enc128blk(mask uint128be, bit uint8, in, out []byte) { // 由于最后一次处理有读取越界, 因此作扩展 var buf [16]byte @@ -158,6 +163,7 @@ func enc128blk(mask uint128be, bit uint8, in, out []byte) { // enc64blk for bit 6 10 12 14 // len(in)>0, len(out)==len(in)/bit*16 +//go:nosplit func enc64blk(mask uint64, bit uint8, in, out []byte) { // 由于最后一次处理有读取越界, 因此作扩展 var buf [8]byte