├── .gitignore ├── LICENSE ├── README.md ├── docs ├── compareVarIntAndStd.ods └── data.txt ├── go.mod ├── img ├── benchmarkEncodeAndDecode.png └── benchmarks.png ├── varint.go └── varint_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.ods# 2 | varint.test 3 | objdump.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Christophe Meessen 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Variable length unsigned integer encoding 2 | 3 | Small unsigned integer values are more frequent than big values. A substantial 4 | compression may then be achieved by dropping the insignificant 0 bits when 5 | serializing integers in a byte sequence. The byte length of the encoded 6 | integer will then vary according to the number of significant bits. 7 | 8 | The length of the encoded integer must be encoded with the integer bits so 9 | that the original integer can be restored. There exist different ways to 10 | encode a varying length integer. Here we consider only the encoding of `uint64` 11 | values. 12 | 13 | The most popular of such encoding is 14 | [LEB128](https://en.wikipedia.org/wiki/LEB128). It allows to encode integers of 15 | any byte size. It is the encoding used in UTF8 characters and in the 16 | `encoding/binary` package for `Uvarint`. The maximum byte length of an `uint64` 17 | encoded in LEB128 is 10 bytes. 18 | 19 | This package implements the 20 | [prefix code](https://en.wikipedia.org/wiki/Prefix_code). The significant bytes 21 | are serialized in big endian order, and the most significant bits of the first 22 | byte set to 1 encode the number of bytes that follow the first byte. This 23 | encoding requires less bit fiddling than the postfiy code to encode or decode. 24 | The maximum byte length of an `uint64` prefix encoded is 9 bytes. 25 | 26 | An alternate encoding is the [postfix code](). In this encoding the significant 27 | bytes are serialized in little endian order. The byte length is encoded as a 28 | number of 0 bits in the last byte which appears first in the encoded byte 29 | sequence. It may seam more performant than the prefix code since words may be 30 | written in memory in one instruction on little endian computers, but the bit 31 | fiddling adds a non nigligible overhead. Benchmarks comparing prefix en postfix 32 | code show indeed that prefix code is more performant when coded in Go. 33 | 34 | There exist other encoding as the one use by SQLight, but we don't discuss them 35 | here. 36 | 37 | ## Usage 38 | 39 | To use this package you must first make sure that you have a `go.mod` file in 40 | your project. To create a `go.mod` file, you must issue the following command in 41 | your terminal where you replace `myProgram` with the name of your program. 42 | 43 | ```bash 44 | $ go mod init myProgram 45 | ``` 46 | 47 | The second step is to import this package in the go source files that will use 48 | its functions. 49 | 50 | ```go 51 | import "github.com/chmike/varint" 52 | ``` 53 | 54 | ### Encoding 55 | 56 | To encode a `uint64` value, use the following function. It will serialize the 57 | value in the byte slice and return the number of bytes written. If the slice 58 | is too small to hold the encoded integer, the function returns 0. The 59 | function never panics. 60 | 61 | ```go 62 | func varint.Encode([]byte, uint64) int 63 | ``` 64 | 65 | In your program, you would encode the value `1234` in the byte slice `b` 66 | like this: 67 | 68 | ```go 69 | l := varint.Encode(b, 1234) 70 | if l == 0 { 71 | // b is too small to hold the encoded value 72 | } 73 | b = b[l:] 74 | ``` 75 | 76 | ### Decoding 77 | 78 | To decode an encoded `uint64` value, use the following function. It will deserialize 79 | the value and return the number of bytes read. If the slice is too small to contain 80 | a value (slice empty or value truncated), the function returns the value 0 and 0 bytes 81 | read. The function never panics. 82 | 83 | ```go 84 | func varint.Decode([]byte) (uint64,int) 85 | ``` 86 | 87 | In your program, you would decode an encoded value in the byte slice `b` like this: 88 | 89 | ```go 90 | l, v := varint.Decod(b) 91 | if l == 0 { 92 | // b is empty or the value is truncated 93 | } 94 | b = b[l:] 95 | ``` 96 | ### difference with the Uvarint functions 97 | 98 | These functions have the same API as the `binary.PutUvarint()` and the `binary.Uvarint` 99 | functions and can conveniently replace them. There are a number of differences though. 100 | 101 | - the `Uvarint()` and `PatUvarint` functions may panic 102 | - the function `PutUvarint()` will never return 0 103 | - `Uvarint()` may return a negative number of bytes read in some conditions. 104 | 105 | ## Preformance 106 | 107 | The extensive study of performance comparing different implementation of 108 | postfix and prefix encoding led to this code. Its performance is compared 109 | with the `binary.PutUvarint()` and `binary.Uvarint()` and presented in 110 | the following graphic. The data is made available in `docs/data.txt`. 111 | 112 | ![benchmarks](img/benchmarks.png) 113 | 114 | The LEB128 encoding with `binary.PutUvarint()` for values up to 35 bits 115 | is as fast as `varint.Encode()` function. Encoding values smaller than 116 | 128 is inlined. `varint.Decode()` of such values is slower than `Uvarint` 117 | because it is not inlined by the compiler. 118 | 119 | For unknown reason the LEB128 encoding and decoding functions become 120 | significantly slower for values bigger than 35bits on my computer. I 121 | don't know if it's due to the compiler or the CPU. 122 | 123 | The following graphic shows the time required to encode and decode a 124 | `uint64` value. This clearly shows that `varint` is faster. Since a value 125 | is usually written once and read many times, it shows that prefix encoding 126 | is a more efficient encoding than LEB128. 127 | 128 | ![benchmark encode and decode](img/benchmarkEncodeAndDecode.png) 129 | 130 | ### Note of caution on the benchmark results 131 | 132 | The benchmark is advantaging the `chmike/varint` code due to branch prediction 133 | optimisation. Encoding a slice of uint64 values of different magnitude would 134 | mitigate this effect. A zipf distributed magnitude might be more realist. 135 | 136 | Any other suggestion for a better benchmarking method is welcome in the issue 137 | section. 138 | -------------------------------------------------------------------------------- /docs/compareVarIntAndStd.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chmike/varint/231d58021b2711574ef0c96ec0d2a212f610b864/docs/compareVarIntAndStd.ods -------------------------------------------------------------------------------- /docs/data.txt: -------------------------------------------------------------------------------- 1 | $ go test -bench=. 2 | goos: linux 3 | goarch: amd64 4 | pkg: github.com/chmike/varint 5 | cpu: 11th Gen Intel(R) Core(TM) i5-11400 @ 2.60GHz 6 | BenchmarkEncode/bits=7-12 1000000000 0.6907 ns/op 7 | BenchmarkEncode/bits=14-12 932243529 1.292 ns/op 8 | BenchmarkEncode/bits=21-12 756967478 1.535 ns/op 9 | BenchmarkEncode/bits=28-12 725723268 1.646 ns/op 10 | BenchmarkEncode/bits=35-12 547925599 2.122 ns/op 11 | BenchmarkEncode/bits=42-12 501735850 2.349 ns/op 12 | BenchmarkEncode/bits=49-12 398654413 2.639 ns/op 13 | BenchmarkEncode/bits=56-12 443887192 2.599 ns/op 14 | BenchmarkEncode/bits=63-12 395891636 2.992 ns/op 15 | BenchmarkDecode/bits=7-12 1000000000 1.154 ns/op 16 | BenchmarkDecode/bits=14-12 836320203 1.401 ns/op 17 | BenchmarkDecode/bits=21-12 772333564 1.514 ns/op 18 | BenchmarkDecode/bits=28-12 632683725 1.841 ns/op 19 | BenchmarkDecode/bits=35-12 568792237 2.085 ns/op 20 | BenchmarkDecode/bits=42-12 458984338 2.574 ns/op 21 | BenchmarkDecode/bits=49-12 428568614 2.701 ns/op 22 | BenchmarkDecode/bits=56-12 444426855 2.641 ns/op 23 | BenchmarkDecode/bits=63-12 395443143 2.754 ns/op 24 | BenchmarkStdEncode/bits=7-12 1000000000 0.6875 ns/op 25 | BenchmarkStdEncode/bits=14-12 940282766 1.087 ns/op 26 | BenchmarkStdEncode/bits=21-12 837261666 1.375 ns/op 27 | BenchmarkStdEncode/bits=28-12 697451035 1.688 ns/op 28 | BenchmarkStdEncode/bits=35-12 564932743 2.061 ns/op 29 | BenchmarkStdEncode/bits=42-12 511001306 2.305 ns/op 30 | BenchmarkStdEncode/bits=49-12 270235705 4.450 ns/op 31 | BenchmarkStdEncode/bits=56-12 258503060 4.616 ns/op 32 | BenchmarkStdEncode/bits=63-12 235358330 5.051 ns/op 33 | BenchmarkStdDecode/bits=7-12 1000000000 0.9654 ns/op 34 | BenchmarkStdDecode/bits=14-12 841473596 1.456 ns/op 35 | BenchmarkStdDecode/bits=21-12 709707164 1.664 ns/op 36 | BenchmarkStdDecode/bits=28-12 569125220 2.102 ns/op 37 | BenchmarkStdDecode/bits=35-12 513172311 2.322 ns/op 38 | BenchmarkStdDecode/bits=42-12 281090527 4.685 ns/op 39 | BenchmarkStdDecode/bits=49-12 209150473 5.145 ns/op 40 | BenchmarkStdDecode/bits=56-12 226411513 5.084 ns/op 41 | BenchmarkStdDecode/bits=63-12 217421734 5.510 ns/op 42 | BenchmarkReadWrite/bits=7-12 773950257 1.521 ns/op 43 | BenchmarkReadWrite/bits=14-12 510693825 2.343 ns/op 44 | BenchmarkReadWrite/bits=21-12 397130238 3.000 ns/op 45 | BenchmarkReadWrite/bits=28-12 367378353 3.247 ns/op 46 | BenchmarkReadWrite/bits=35-12 284325730 4.150 ns/op 47 | BenchmarkReadWrite/bits=42-12 244221192 4.733 ns/op 48 | BenchmarkReadWrite/bits=49-12 220132791 5.218 ns/op 49 | BenchmarkReadWrite/bits=56-12 223831064 5.262 ns/op 50 | BenchmarkReadWrite/bits=63-12 206731152 5.726 ns/op 51 | BenchmarkStdReadWrite/bits=7-12 731706678 1.604 ns/op 52 | BenchmarkStdReadWrite/bits=14-12 552907916 2.143 ns/op 53 | BenchmarkStdReadWrite/bits=21-12 403626302 2.913 ns/op 54 | BenchmarkStdReadWrite/bits=28-12 196490768 5.979 ns/op 55 | BenchmarkStdReadWrite/bits=35-12 159500511 7.670 ns/op 56 | BenchmarkStdReadWrite/bits=42-12 172616569 6.894 ns/op 57 | BenchmarkStdReadWrite/bits=49-12 148928010 8.121 ns/op 58 | BenchmarkStdReadWrite/bits=56-12 139254903 8.432 ns/op 59 | BenchmarkStdReadWrite/bits=63-12 146276461 8.256 ns/op 60 | PASS 61 | ok github.com/chmike/varint 80.411s 62 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/chmike/varint 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /img/benchmarkEncodeAndDecode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chmike/varint/231d58021b2711574ef0c96ec0d2a212f610b864/img/benchmarkEncodeAndDecode.png -------------------------------------------------------------------------------- /img/benchmarks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chmike/varint/231d58021b2711574ef0c96ec0d2a212f610b864/img/benchmarks.png -------------------------------------------------------------------------------- /varint.go: -------------------------------------------------------------------------------- 1 | package varint 2 | 3 | import "math/bits" 4 | 5 | // Encode v as a prefixed with 1 bits variable length integer into b. 6 | // Returns the number of bytes written or 0 if b is too small. 7 | // The encoded integer is at most 9 bytes long. This function doesn't 8 | // panic. 9 | func Encode(b []byte, v uint64) int { 10 | if v < 0x80 && len(b) != 0 { 11 | b[0] = byte(v) 12 | return 1 13 | } 14 | return encodeSlow(b, v) 15 | } 16 | 17 | func encodeSlow(b []byte, v uint64) int { 18 | switch { 19 | case v < 1<<14: 20 | if len(b) >= 2 { 21 | x := bits.ReverseBytes16(uint16(v) | 0x8000) 22 | b[0] = byte(x) 23 | b[1] = byte(x >> 8) 24 | return 2 25 | } 26 | case v < 1<<21: 27 | if len(b) >= 3 { 28 | x := bits.ReverseBytes32(uint32(v)|0xC00000) >> 8 29 | b[0] = byte(x) 30 | b[1] = byte(x >> 8) 31 | b[2] = byte(x >> 16) 32 | return 3 33 | } 34 | case v < 1<<28: 35 | if len(b) >= 4 { 36 | x := bits.ReverseBytes32(uint32(v) | 0xE0000000) 37 | b[0] = byte(x) 38 | b[1] = byte(x >> 8) 39 | b[2] = byte(x >> 16) 40 | b[3] = byte(x >> 24) 41 | return 4 42 | } 43 | case v < 1<<35: 44 | if len(b) >= 5 { 45 | x := bits.ReverseBytes64(v|0xF000000000) >> 24 46 | b[0] = byte(x) 47 | b[1] = byte(x >> 8) 48 | b[2] = byte(x >> 16) 49 | b[3] = byte(x >> 24) 50 | b[4] = byte(x >> 32) 51 | return 5 52 | } 53 | case v < 1<<42: 54 | if len(b) >= 6 { 55 | x := bits.ReverseBytes64(v|0xF80000000000) >> 16 56 | b[0] = byte(x) 57 | b[1] = byte(x >> 8) 58 | b[2] = byte(x >> 16) 59 | b[3] = byte(x >> 24) 60 | b[4] = byte(x >> 32) 61 | b[5] = byte(x >> 40) 62 | return 6 63 | } 64 | case v < 1<<49: 65 | if len(b) >= 7 { 66 | x := bits.ReverseBytes64(v|0xFC000000000000) >> 8 67 | b[0] = byte(x) 68 | b[1] = byte(x >> 8) 69 | b[2] = byte(x >> 16) 70 | b[3] = byte(x >> 24) 71 | b[4] = byte(x >> 32) 72 | b[5] = byte(x >> 40) 73 | b[6] = byte(x >> 48) 74 | return 7 75 | } 76 | case v < 1<<56: 77 | if len(b) >= 8 { 78 | x := bits.ReverseBytes64(v | 0xFE00000000000000) 79 | b[0] = byte(x) 80 | b[1] = byte(x >> 8) 81 | b[2] = byte(x >> 16) 82 | b[3] = byte(x >> 24) 83 | b[4] = byte(x >> 32) 84 | b[5] = byte(x >> 40) 85 | b[6] = byte(x >> 48) 86 | b[7] = byte(x >> 56) 87 | return 8 88 | } 89 | default: 90 | if len(b) >= 9 { 91 | x := bits.ReverseBytes64(v) 92 | b[0] = 0xFF 93 | b[1] = byte(x) 94 | b[2] = byte(x >> 8) 95 | b[3] = byte(x >> 16) 96 | b[4] = byte(x >> 24) 97 | b[5] = byte(x >> 32) 98 | b[6] = byte(x >> 40) 99 | b[7] = byte(x >> 48) 100 | b[8] = byte(x >> 56) 101 | return 9 102 | } 103 | } 104 | return 0 105 | } 106 | 107 | // Decode returns the varying length integer in front of b and the 108 | // number of bytes read or zero if b is empty or the integer is 109 | // truncated. This function doesn't panic. 110 | func Decode(b []byte) (uint64, int) { 111 | lb := len(b) 112 | if lb > 0 { 113 | hdr := b[0] 114 | switch { 115 | case hdr <= 0x7F: 116 | return uint64(hdr), 1 117 | case hdr <= 0xBF: 118 | if lb >= 2 { 119 | return uint64(hdr&0x3F)<<8 | uint64(b[1]), 2 120 | } 121 | case hdr <= 0xDF: 122 | if lb >= 3 { 123 | return uint64(hdr&0x1F)<<16 | uint64(b[1])<<8 | uint64(b[2]), 3 124 | } 125 | case hdr <= 0xEF: 126 | if lb >= 4 { 127 | return uint64(hdr&0x0F)<<24 | uint64(b[1])<<16 | uint64(b[2])<<8 | uint64(b[3]), 4 128 | } 129 | case hdr <= 0xF7: 130 | if lb >= 5 { 131 | return uint64(hdr&0x07)<<32 | uint64(b[1])<<24 | uint64(b[2])<<16 | uint64(b[3])<<8 | 132 | uint64(b[4]), 5 133 | } 134 | case hdr <= 0xFB: 135 | if lb >= 6 { 136 | return uint64(hdr&0x03)<<40 | uint64(b[1])<<32 | uint64(b[2])<<24 | uint64(b[3])<<16 | 137 | uint64(b[4])<<8 | uint64(b[5]), 6 138 | } 139 | case hdr <= 0xFD: 140 | if lb >= 7 { 141 | return uint64(hdr&0x01)<<48 | uint64(b[1])<<40 | uint64(b[2])<<32 | uint64(b[3])<<24 | 142 | uint64(b[4])<<16 | uint64(b[5])<<8 | uint64(b[6]), 7 143 | } 144 | case hdr <= 0xFE: 145 | if lb >= 8 { 146 | return uint64(b[1])<<48 | uint64(b[2])<<40 | uint64(b[3])<<32 | uint64(b[4])<<24 | 147 | uint64(b[5])<<16 | uint64(b[6])<<8 | uint64(b[7]), 8 148 | } 149 | default: 150 | if lb >= 9 { 151 | return uint64(b[1])<<56 | uint64(b[2])<<48 | uint64(b[3])<<40 | uint64(b[4])<<32 | 152 | uint64(b[5])<<24 | uint64(b[6])<<16 | uint64(b[7])<<8 | uint64(b[8]), 9 153 | } 154 | } 155 | } 156 | return 0, 0 157 | } 158 | -------------------------------------------------------------------------------- /varint_test.go: -------------------------------------------------------------------------------- 1 | package varint 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "math/rand" 7 | "testing" 8 | ) 9 | 10 | func TestVarInt(t *testing.T) { 11 | var buf [16]byte 12 | for bits := 0; bits <= 64; bits++ { 13 | var value uint64 = 0xAAAAAAAAAAAAAAAA >> (64 - bits) 14 | t.Run(fmt.Sprintf("bits_%d-", bits), func(t *testing.T) { 15 | n1 := Encode(buf[:], value) 16 | out, n2 := Decode(buf[:]) 17 | if value != out { 18 | t.Fatalf("bits %d, expect value %X, got value %X", bits, value, out) 19 | } 20 | if n1 != n2 { 21 | t.Fatalf("bits %d, expect length %d, got length %d", bits, n1, n2) 22 | } 23 | }) 24 | } 25 | } 26 | 27 | func TestTruncated(t *testing.T) { 28 | var buf []byte 29 | if l := Encode(buf, 1234); l != 0 { 30 | t.Fatalf("expected 0, got %d", l) 31 | } 32 | if l, v := Decode(buf); l != 0 || v != 0 { 33 | t.Fatalf("expected 0 and 0, got %d and %d", l, v) 34 | } 35 | } 36 | 37 | var out int 38 | var buf0 = make([]byte, 1024) 39 | var buf = buf0[:rand.Int()%1010] 40 | 41 | func BenchmarkEncode(b *testing.B) { 42 | for nBits := 7; nBits < 64; nBits += 7 { 43 | var value uint64 = 0xAAAAAAAAAAAAAAAA >> (64 - nBits) 44 | b.Run(fmt.Sprintf("bits=%d", nBits), func(b *testing.B) { 45 | for n := 0; n < b.N; n++ { 46 | out = Encode(buf, value) 47 | } 48 | }) 49 | } 50 | } 51 | 52 | func BenchmarkDecode(b *testing.B) { 53 | for nBits := 7; nBits < 64; nBits += 7 { 54 | var value uint64 = 0xAAAAAAAAAAAAAAAA >> (64 - nBits) 55 | Encode(buf, value) 56 | b.Run(fmt.Sprintf("bits=%d", nBits), func(b *testing.B) { 57 | for n := 0; n < b.N; n++ { 58 | _, out = Decode(buf) 59 | } 60 | }) 61 | } 62 | } 63 | 64 | func BenchmarkStdEncode(b *testing.B) { 65 | for nBits := 7; nBits < 64; nBits += 7 { 66 | var value uint64 = 0xAAAAAAAAAAAAAAAA >> (64 - nBits) 67 | b.Run(fmt.Sprintf("bits=%d", nBits), func(b *testing.B) { 68 | for n := 0; n < b.N; n++ { 69 | out = binary.PutUvarint(buf, value) 70 | } 71 | }) 72 | } 73 | } 74 | 75 | func BenchmarkStdDecode(b *testing.B) { 76 | for nBits := 7; nBits < 64; nBits += 7 { 77 | var value uint64 = 0xAAAAAAAAAAAAAAAA >> (64 - nBits) 78 | binary.PutUvarint(buf, value) 79 | b.Run(fmt.Sprintf("bits=%d", nBits), func(b *testing.B) { 80 | for n := 0; n < b.N; n++ { 81 | _, out = binary.Uvarint(buf) 82 | } 83 | }) 84 | } 85 | } 86 | 87 | func BenchmarkReadWrite(b *testing.B) { 88 | for nBits := 7; nBits < 64; nBits += 7 { 89 | var value uint64 = 0xAAAAAAAAAAAAAAAA >> (64 - nBits) 90 | b.Run(fmt.Sprintf("bits=%d", nBits), func(b *testing.B) { 91 | for n := 0; n < b.N; n++ { 92 | Encode(buf, value) 93 | _, out = Decode(buf) 94 | } 95 | }) 96 | } 97 | } 98 | 99 | func BenchmarkStdReadWrite(b *testing.B) { 100 | for nBits := 7; nBits < 64; nBits += 7 { 101 | var value uint64 = 0xAAAAAAAAAAAAAAAA >> (64 - nBits) 102 | b.Run(fmt.Sprintf("bits=%d", nBits), func(b *testing.B) { 103 | for n := 0; n < b.N; n++ { 104 | binary.PutUvarint(buf, value) 105 | _, out = binary.Uvarint(buf) 106 | } 107 | }) 108 | } 109 | } 110 | --------------------------------------------------------------------------------