diff --git a/config.json b/config.json index 9ff4af306..48804fb69 100644 --- a/config.json +++ b/config.json @@ -74,7 +74,8 @@ "word-search", "connect", "ledger", - "poker" + "poker", + "variable-length-quantity" ], "deprecated": [ "accumulate", diff --git a/exercises/variable-length-quantity/example.go b/exercises/variable-length-quantity/example.go new file mode 100644 index 000000000..945c4966b --- /dev/null +++ b/exercises/variable-length-quantity/example.go @@ -0,0 +1,56 @@ +package variablelengthquantity + +// EncodeVarint returns the varint encoding of x. +func EncodeVarint(x uint32) []byte { + if x>>7 == 0 { + return []byte{ + byte(x), + } + } + + if x>>14 == 0 { + return []byte{ + byte(0x80 | x>>7), + byte(127 & x), + } + } + + if x>>21 == 0 { + return []byte{ + byte(0x80 | x>>14), + byte(0x80 | x>>7), + byte(127 & x), + } + } + + return []byte{ + byte(0x80 | x>>21), + byte(0x80 | x>>14), + byte(0x80 | x>>7), + byte(127 & x), + } +} + +// DecodeVarint reads a varint-encoded integer from the slice. +// It returns the integer and the number of bytes consumed, or +// zero if there is not enough. +func DecodeVarint(buf []byte) (x uint32, n int) { + if len(buf) < 1 { + return 0, 0 + } + + if buf[0] <= 0x80 { + return uint32(buf[0]), 1 + } + + var b byte + for n, b = range buf { + x = x << 7 + x |= uint32(b) & 0x7F + if (b & 0x80) == 0 { + return x, n + } + } + + return x, n +} diff --git a/exercises/variable-length-quantity/variable_length_quantity_test.go b/exercises/variable-length-quantity/variable_length_quantity_test.go new file mode 100644 index 000000000..66f7d22eb --- /dev/null +++ b/exercises/variable-length-quantity/variable_length_quantity_test.go @@ -0,0 +1,36 @@ +package variablelengthquantity + +import ( + "bytes" + "testing" +) + +func TestEncodeDecodeVarint(t *testing.T) { + testCases := []struct { + input []byte + output uint32 + }{ + 0: {[]byte{0x7F}, 127}, + 1: {[]byte{0x81, 0x00}, 128}, + 2: {[]byte{0xC0, 0x00}, 8192}, + 3: {[]byte{0xFF, 0x7F}, 16383}, + 4: {[]byte{0x81, 0x80, 0x00}, 16384}, + 5: {[]byte{0xFF, 0xFF, 0x7F}, 2097151}, + 6: {[]byte{0x81, 0x80, 0x80, 0x00}, 2097152}, + 7: {[]byte{0xC0, 0x80, 0x80, 0x00}, 134217728}, + 8: {[]byte{0xFF, 0xFF, 0xFF, 0x7F}, 268435455}, + + 9: {[]byte{0x82, 0x00}, 256}, + 10: {[]byte{0x81, 0x10}, 144}, + } + + for i, tc := range testCases { + t.Logf("test case %d - %#v\n", i, tc.input) + if o, _ := DecodeVarint(tc.input); o != tc.output { + t.Fatalf("expected %d\ngot\n%d\n", tc.output, o) + } + if encoded := EncodeVarint(tc.output); bytes.Compare(encoded, tc.input) != 0 { + t.Fatalf("%d - expected %#v\ngot\n%#v\n", tc.output, tc.input, encoded) + } + } +}