-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add general serialization for natural numbers (#41)
- Loading branch information
Showing
9 changed files
with
223 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package jam | ||
|
||
import "errors" | ||
|
||
var ( | ||
// errFirstByteNineByteSerialization is returned when the first byte has wrong value in 9-byte serialization | ||
errFirstByteNineByteSerialization = errors.New("expected first byte to be 255 for 9-byte serialization") | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package jam | ||
|
||
import ( | ||
"encoding/binary" | ||
"math" | ||
"math/bits" | ||
) | ||
|
||
// GeneralNatural implements the formula (275: able to encode naturals of up to 2^64) | ||
type GeneralNatural struct{} | ||
|
||
func (j *GeneralNatural) SerializeUint64(x uint64) []byte { | ||
var l uint8 | ||
// Determine the length needed to represent the value | ||
for l = 0; l < 8; l++ { | ||
if x < (1 << (7 * (l + 1))) { | ||
break | ||
} | ||
} | ||
bytes := make([]byte, 0) | ||
if l < 8 { | ||
// Calculate the prefix byte, ensure it stays within uint8 range | ||
prefix := uint8((256 - (1 << (8 - l))) + (x>>(8*l))&math.MaxUint8) | ||
bytes = append(bytes, prefix) | ||
} else { | ||
bytes = append(bytes, math.MaxUint8) | ||
} | ||
// Serialize the integer in little-endian order | ||
for i := 0; i < int(l); i++ { | ||
byteVal := uint8((x >> (8 * i)) & math.MaxUint8) | ||
bytes = append(bytes, byteVal) | ||
} | ||
return bytes | ||
} | ||
|
||
// DeserializeUint64 deserializes a byte slice into a uint64 value. | ||
func (j *GeneralNatural) DeserializeUint64(serialized []byte, u *uint64) error { | ||
*u = 0 | ||
|
||
n := len(serialized) | ||
if n == 0 { | ||
return nil | ||
} | ||
|
||
if n > 8 { | ||
if serialized[0] != math.MaxUint8 { | ||
return errFirstByteNineByteSerialization | ||
} | ||
*u = binary.LittleEndian.Uint64(serialized[1:9]) | ||
return nil | ||
} | ||
|
||
prefix := serialized[0] | ||
l := uint8(bits.LeadingZeros8(^prefix)) | ||
|
||
// Deserialize the first `l` bytes | ||
for i := uint8(0); i < l; i++ { | ||
*u |= uint64(serialized[i+1]) << (8 * i) | ||
} | ||
|
||
// Combine the remaining part of the prefix | ||
*u |= uint64(prefix&(math.MaxUint8>>l)) << (8 * l) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package jam | ||
|
||
import ( | ||
"fmt" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"math" | ||
"testing" | ||
) | ||
|
||
func TestEncodeDecodeUint64(t *testing.T) { | ||
testCases := []struct { | ||
input uint64 | ||
expected []byte | ||
}{ | ||
// l = 0 | ||
{0, []byte{0}}, | ||
{1, []byte{1}}, | ||
{math.MaxInt8, []byte{127}}, // 127 | ||
// l = 1 | ||
{1 << 7, []byte{128, 128}}, // 128 | ||
{math.MaxUint8, []byte{128, 255}}, // 255 | ||
{1 << 8, []byte{129, 0}}, // 256 | ||
{(1 << 10) - 1, []byte{131, 255}}, // 1023 | ||
{1 << 10, []byte{132, 0}}, // 1024 | ||
{(1 << 14) - 1, []byte{191, 255}}, // 16383 | ||
//l = 2 | ||
{1 << 14, []byte{192, 0, 64}}, // 16384 | ||
{math.MaxUint16, []byte{192, 255, 255}}, // 65535 | ||
{1 << 16, []byte{193, 0, 0}}, // 65536 | ||
{(1 << 21) - 1, []byte{223, 255, 255}}, // 2097151 | ||
//l = 3 | ||
{1 << 21, []byte{224, 0, 0, 32}}, // 2097152 | ||
{(1 << 28) - 1, []byte{239, 255, 255, 255}}, // 268435455 | ||
//l = 4 | ||
{1 << 28, []byte{240, 0, 0, 0, 16}}, // 268435456 | ||
{(1 << 35) - 1, []byte{247, 255, 255, 255, 255}}, // 34359738367 | ||
//l = 5 | ||
{1 << 35, []byte{248, 0, 0, 0, 0, 8}}, // 34359738368 | ||
{(1 << 42) - 1, []byte{251, 255, 255, 255, 255, 255}}, // 4398046511103 | ||
//l = 6 | ||
{1 << 42, []byte{252, 0, 0, 0, 0, 0, 4}}, // 4398046511104 | ||
{(1 << 49) - 1, []byte{253, 255, 255, 255, 255, 255, 255}}, // 562949953421311 | ||
//l = 7 | ||
{1 << 49, []byte{254, 0, 0, 0, 0, 0, 0, 2}}, // 562949953421312 | ||
{(1 << 56) - 1, []byte{254, 255, 255, 255, 255, 255, 255, 255}}, // 72057594037927935 | ||
// l = 8 | ||
{1 << 56, []byte{255, 0, 0, 0, 0, 0, 0, 0, 1}}, // 72057594037927936 | ||
{1 << 63, []byte{255, 0, 0, 0, 0, 0, 0, 0, 128}}, // 9223372036854775808 | ||
} | ||
|
||
gn := GeneralNatural{} | ||
|
||
for _, tc := range testCases { | ||
t.Run(fmt.Sprintf("uint64(%d)", tc.input), func(t *testing.T) { | ||
// Marshal the input value | ||
serialized := gn.SerializeUint64(tc.input) | ||
|
||
// Check if the serialized output matches the expected output | ||
assert.Equal(t, tc.expected, serialized, "serialized output mismatch for input %d", tc.input) | ||
|
||
// Unmarshal the serialized data back into a uint64 | ||
var deserialized uint64 | ||
err := gn.DeserializeUint64(serialized, &deserialized) | ||
require.NoError(t, err, "unmarshal(%v) returned an unexpected error", serialized) | ||
|
||
// Check if the deserialized value matches the original input | ||
assert.Equal(t, tc.input, deserialized, "deserialized value mismatch for input %d", tc.input) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package codec | ||
|
||
import ( | ||
"errors" | ||
"github.com/eigerco/strawberry/pkg/serialization/codec/jam" | ||
) | ||
|
||
// JAMCodec implements the Codec interface for JSON encoding and decoding. | ||
type JAMCodec struct { | ||
gn jam.GeneralNatural | ||
} | ||
|
||
// NewJamCodec initializes an instance of Jam codec | ||
func NewJamCodec() *JAMCodec { | ||
return &JAMCodec{gn: jam.GeneralNatural{}} | ||
} | ||
|
||
func (j *JAMCodec) Marshal(v interface{}) ([]byte, error) { | ||
// TODO | ||
return nil, errors.New("not implemented") | ||
} | ||
|
||
func (j *JAMCodec) MarshalGeneral(v uint64) ([]byte, error) { | ||
return j.gn.SerializeUint64(v), nil | ||
} | ||
|
||
func (j *JAMCodec) Unmarshal(data []byte, v interface{}) error { | ||
// TODO | ||
return errors.New("not implemented") | ||
} | ||
|
||
func (j *JAMCodec) UnmarshalGeneral(data []byte, v *uint64) error { | ||
return j.gn.DeserializeUint64(data, v) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters