Joe Tsai | 22505a4 | 2018-08-01 13:12:49 -0700 | [diff] [blame] | 1 | // Copyright 2018 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | package wire |
| 6 | |
| 7 | import ( |
| 8 | "bytes" |
| 9 | "encoding/hex" |
| 10 | "io" |
| 11 | "math" |
| 12 | "strings" |
| 13 | "testing" |
| 14 | ) |
| 15 | |
| 16 | type ( |
| 17 | testOps struct { |
| 18 | // appendOps is a sequence of append operations, each appending to |
| 19 | // the output of the previous append operation. |
| 20 | appendOps []appendOp |
| 21 | |
| 22 | // wantRaw (if not nil) is the bytes that the appendOps should produce. |
| 23 | wantRaw []byte |
| 24 | |
| 25 | // consumeOps are a sequence of consume operations, each consuming the |
| 26 | // remaining output after the previous consume operation. |
| 27 | // The first consume operation starts with the output of appendOps. |
| 28 | consumeOps []consumeOp |
| 29 | } |
| 30 | |
| 31 | // appendOp represents an Append operation. |
| 32 | appendOp = interface{} |
| 33 | appendTag struct { |
| 34 | inNum Number |
| 35 | inType Type |
| 36 | } |
| 37 | appendVarint struct { |
| 38 | inVal uint64 |
| 39 | } |
| 40 | appendFixed32 struct { |
| 41 | inVal uint32 |
| 42 | } |
| 43 | appendFixed64 struct { |
| 44 | inVal uint64 |
| 45 | } |
| 46 | appendBytes struct { |
| 47 | inVal []byte |
| 48 | } |
| 49 | appendGroup struct { |
| 50 | inNum Number |
| 51 | inVal []byte |
| 52 | } |
| 53 | appendRaw []byte |
| 54 | |
| 55 | // consumeOp represents an Consume operation. |
| 56 | consumeOp = interface{} |
| 57 | consumeField struct { |
| 58 | wantNum Number |
| 59 | wantType Type |
| 60 | wantCnt int |
| 61 | wantErr error |
| 62 | } |
| 63 | consumeFieldValue struct { |
| 64 | inNum Number |
| 65 | inType Type |
| 66 | wantCnt int |
| 67 | wantErr error |
| 68 | } |
| 69 | consumeTag struct { |
| 70 | wantNum Number |
| 71 | wantType Type |
| 72 | wantCnt int |
| 73 | wantErr error |
| 74 | } |
| 75 | consumeVarint struct { |
| 76 | wantVal uint64 |
| 77 | wantCnt int |
| 78 | wantErr error |
| 79 | } |
| 80 | consumeFixed32 struct { |
| 81 | wantVal uint32 |
| 82 | wantCnt int |
| 83 | wantErr error |
| 84 | } |
| 85 | consumeFixed64 struct { |
| 86 | wantVal uint64 |
| 87 | wantCnt int |
| 88 | wantErr error |
| 89 | } |
| 90 | consumeBytes struct { |
| 91 | wantVal []byte |
| 92 | wantCnt int |
| 93 | wantErr error |
| 94 | } |
| 95 | consumeGroup struct { |
| 96 | inNum Number |
| 97 | wantVal []byte |
| 98 | wantCnt int |
| 99 | wantErr error |
| 100 | } |
| 101 | |
| 102 | ops []interface{} |
| 103 | ) |
| 104 | |
| 105 | // dhex decodes a hex-string and returns the bytes and panics if s is invalid. |
| 106 | func dhex(s string) []byte { |
| 107 | b, err := hex.DecodeString(s) |
| 108 | if err != nil { |
| 109 | panic(err) |
| 110 | } |
| 111 | return b |
| 112 | } |
| 113 | |
| 114 | func TestTag(t *testing.T) { |
| 115 | runTests(t, []testOps{{ |
| 116 | appendOps: ops{appendRaw(dhex(""))}, |
| 117 | consumeOps: ops{consumeTag{wantErr: io.ErrUnexpectedEOF}}, |
| 118 | }, { |
| 119 | appendOps: ops{appendTag{inNum: 0, inType: Fixed32Type}}, |
| 120 | wantRaw: dhex("05"), |
| 121 | consumeOps: ops{consumeTag{wantErr: errFieldNumber}}, |
| 122 | }, { |
| 123 | appendOps: ops{appendTag{inNum: 1, inType: Fixed32Type}}, |
| 124 | wantRaw: dhex("0d"), |
| 125 | consumeOps: ops{consumeTag{wantNum: 1, wantType: Fixed32Type, wantCnt: 1}}, |
| 126 | }, { |
| 127 | appendOps: ops{appendTag{inNum: FirstReservedNumber, inType: BytesType}}, |
| 128 | wantRaw: dhex("c2a309"), |
| 129 | consumeOps: ops{consumeTag{wantNum: FirstReservedNumber, wantType: BytesType, wantCnt: 3}}, |
| 130 | }, { |
| 131 | appendOps: ops{appendTag{inNum: LastReservedNumber, inType: StartGroupType}}, |
| 132 | wantRaw: dhex("fbe109"), |
| 133 | consumeOps: ops{consumeTag{wantNum: LastReservedNumber, wantType: StartGroupType, wantCnt: 3}}, |
| 134 | }, { |
| 135 | appendOps: ops{appendTag{inNum: MaxValidNumber, inType: VarintType}}, |
| 136 | wantRaw: dhex("f8ffffff0f"), |
| 137 | consumeOps: ops{consumeTag{wantNum: MaxValidNumber, wantType: VarintType, wantCnt: 5}}, |
| 138 | }, { |
| 139 | appendOps: ops{appendTag{inNum: MaxValidNumber + 1, inType: VarintType}}, |
| 140 | wantRaw: dhex("8080808010"), |
| 141 | consumeOps: ops{consumeTag{wantErr: errFieldNumber}}, |
| 142 | }}) |
| 143 | } |
| 144 | |
| 145 | func TestVarint(t *testing.T) { |
| 146 | runTests(t, []testOps{{ |
| 147 | appendOps: ops{appendRaw(dhex(""))}, |
| 148 | consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, |
| 149 | }, { |
| 150 | appendOps: ops{appendRaw(dhex("80"))}, |
| 151 | consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, |
| 152 | }, { |
| 153 | appendOps: ops{appendRaw(dhex("8080"))}, |
| 154 | consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, |
| 155 | }, { |
| 156 | appendOps: ops{appendRaw(dhex("808080"))}, |
| 157 | consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, |
| 158 | }, { |
| 159 | appendOps: ops{appendRaw(dhex("80808080"))}, |
| 160 | consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, |
| 161 | }, { |
| 162 | appendOps: ops{appendRaw(dhex("8080808080"))}, |
| 163 | consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, |
| 164 | }, { |
| 165 | appendOps: ops{appendRaw(dhex("808080808080"))}, |
| 166 | consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, |
| 167 | }, { |
| 168 | appendOps: ops{appendRaw(dhex("80808080808080"))}, |
| 169 | consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, |
| 170 | }, { |
| 171 | appendOps: ops{appendRaw(dhex("8080808080808080"))}, |
| 172 | consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, |
| 173 | }, { |
| 174 | appendOps: ops{appendRaw(dhex("808080808080808080"))}, |
| 175 | consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, |
| 176 | }, { |
| 177 | appendOps: ops{appendRaw(dhex("80808080808080808080"))}, |
| 178 | consumeOps: ops{consumeVarint{wantErr: errOverflow}}, |
| 179 | }, { |
| 180 | // Test varints at various boundaries where the length changes. |
| 181 | appendOps: ops{appendVarint{inVal: 0x0}}, |
| 182 | wantRaw: dhex("00"), |
| 183 | consumeOps: ops{consumeVarint{wantVal: 0, wantCnt: 1}}, |
| 184 | }, { |
| 185 | appendOps: ops{appendVarint{inVal: 0x1}}, |
| 186 | wantRaw: dhex("01"), |
| 187 | consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 1}}, |
| 188 | }, { |
| 189 | appendOps: ops{appendVarint{inVal: 0x7f}}, |
| 190 | wantRaw: dhex("7f"), |
| 191 | consumeOps: ops{consumeVarint{wantVal: 0x7f, wantCnt: 1}}, |
| 192 | }, { |
| 193 | appendOps: ops{appendVarint{inVal: 0x7f + 1}}, |
| 194 | wantRaw: dhex("8001"), |
| 195 | consumeOps: ops{consumeVarint{wantVal: 0x7f + 1, wantCnt: 2}}, |
| 196 | }, { |
| 197 | appendOps: ops{appendVarint{inVal: 0x3fff}}, |
| 198 | wantRaw: dhex("ff7f"), |
| 199 | consumeOps: ops{consumeVarint{wantVal: 0x3fff, wantCnt: 2}}, |
| 200 | }, { |
| 201 | appendOps: ops{appendVarint{inVal: 0x3fff + 1}}, |
| 202 | wantRaw: dhex("808001"), |
| 203 | consumeOps: ops{consumeVarint{wantVal: 0x3fff + 1, wantCnt: 3}}, |
| 204 | }, { |
| 205 | appendOps: ops{appendVarint{inVal: 0x1fffff}}, |
| 206 | wantRaw: dhex("ffff7f"), |
| 207 | consumeOps: ops{consumeVarint{wantVal: 0x1fffff, wantCnt: 3}}, |
| 208 | }, { |
| 209 | appendOps: ops{appendVarint{inVal: 0x1fffff + 1}}, |
| 210 | wantRaw: dhex("80808001"), |
| 211 | consumeOps: ops{consumeVarint{wantVal: 0x1fffff + 1, wantCnt: 4}}, |
| 212 | }, { |
| 213 | appendOps: ops{appendVarint{inVal: 0xfffffff}}, |
| 214 | wantRaw: dhex("ffffff7f"), |
| 215 | consumeOps: ops{consumeVarint{wantVal: 0xfffffff, wantCnt: 4}}, |
| 216 | }, { |
| 217 | appendOps: ops{appendVarint{inVal: 0xfffffff + 1}}, |
| 218 | wantRaw: dhex("8080808001"), |
| 219 | consumeOps: ops{consumeVarint{wantVal: 0xfffffff + 1, wantCnt: 5}}, |
| 220 | }, { |
| 221 | appendOps: ops{appendVarint{inVal: 0x7ffffffff}}, |
| 222 | wantRaw: dhex("ffffffff7f"), |
| 223 | consumeOps: ops{consumeVarint{wantVal: 0x7ffffffff, wantCnt: 5}}, |
| 224 | }, { |
| 225 | appendOps: ops{appendVarint{inVal: 0x7ffffffff + 1}}, |
| 226 | wantRaw: dhex("808080808001"), |
| 227 | consumeOps: ops{consumeVarint{wantVal: 0x7ffffffff + 1, wantCnt: 6}}, |
| 228 | }, { |
| 229 | appendOps: ops{appendVarint{inVal: 0x3ffffffffff}}, |
| 230 | wantRaw: dhex("ffffffffff7f"), |
| 231 | consumeOps: ops{consumeVarint{wantVal: 0x3ffffffffff, wantCnt: 6}}, |
| 232 | }, { |
| 233 | appendOps: ops{appendVarint{inVal: 0x3ffffffffff + 1}}, |
| 234 | wantRaw: dhex("80808080808001"), |
| 235 | consumeOps: ops{consumeVarint{wantVal: 0x3ffffffffff + 1, wantCnt: 7}}, |
| 236 | }, { |
| 237 | appendOps: ops{appendVarint{inVal: 0x1ffffffffffff}}, |
| 238 | wantRaw: dhex("ffffffffffff7f"), |
| 239 | consumeOps: ops{consumeVarint{wantVal: 0x1ffffffffffff, wantCnt: 7}}, |
| 240 | }, { |
| 241 | appendOps: ops{appendVarint{inVal: 0x1ffffffffffff + 1}}, |
| 242 | wantRaw: dhex("8080808080808001"), |
| 243 | consumeOps: ops{consumeVarint{wantVal: 0x1ffffffffffff + 1, wantCnt: 8}}, |
| 244 | }, { |
| 245 | appendOps: ops{appendVarint{inVal: 0xffffffffffffff}}, |
| 246 | wantRaw: dhex("ffffffffffffff7f"), |
| 247 | consumeOps: ops{consumeVarint{wantVal: 0xffffffffffffff, wantCnt: 8}}, |
| 248 | }, { |
| 249 | appendOps: ops{appendVarint{inVal: 0xffffffffffffff + 1}}, |
| 250 | wantRaw: dhex("808080808080808001"), |
| 251 | consumeOps: ops{consumeVarint{wantVal: 0xffffffffffffff + 1, wantCnt: 9}}, |
| 252 | }, { |
| 253 | appendOps: ops{appendVarint{inVal: 0x7fffffffffffffff}}, |
| 254 | wantRaw: dhex("ffffffffffffffff7f"), |
| 255 | consumeOps: ops{consumeVarint{wantVal: 0x7fffffffffffffff, wantCnt: 9}}, |
| 256 | }, { |
| 257 | appendOps: ops{appendVarint{inVal: 0x7fffffffffffffff + 1}}, |
| 258 | wantRaw: dhex("80808080808080808001"), |
| 259 | consumeOps: ops{consumeVarint{wantVal: 0x7fffffffffffffff + 1, wantCnt: 10}}, |
| 260 | }, { |
| 261 | appendOps: ops{appendVarint{inVal: math.MaxUint64}}, |
| 262 | wantRaw: dhex("ffffffffffffffffff01"), |
| 263 | consumeOps: ops{consumeVarint{wantVal: math.MaxUint64, wantCnt: 10}}, |
| 264 | }, { |
| 265 | appendOps: ops{appendRaw(dhex("ffffffffffffffffff02"))}, |
| 266 | consumeOps: ops{consumeVarint{wantErr: errOverflow}}, |
| 267 | }, { |
| 268 | // Test denormalized varints; where the encoding, while valid, is |
| 269 | // larger than necessary. |
| 270 | appendOps: ops{appendRaw(dhex("01"))}, |
| 271 | consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 1}}, |
| 272 | }, { |
| 273 | appendOps: ops{appendRaw(dhex("8100"))}, |
| 274 | consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 2}}, |
| 275 | }, { |
| 276 | appendOps: ops{appendRaw(dhex("818000"))}, |
| 277 | consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 3}}, |
| 278 | }, { |
| 279 | appendOps: ops{appendRaw(dhex("81808000"))}, |
| 280 | consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 4}}, |
| 281 | }, { |
| 282 | appendOps: ops{appendRaw(dhex("8180808000"))}, |
| 283 | consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 5}}, |
| 284 | }, { |
| 285 | appendOps: ops{appendRaw(dhex("818080808000"))}, |
| 286 | consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 6}}, |
| 287 | }, { |
| 288 | appendOps: ops{appendRaw(dhex("81808080808000"))}, |
| 289 | consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 7}}, |
| 290 | }, { |
| 291 | appendOps: ops{appendRaw(dhex("8180808080808000"))}, |
| 292 | consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 8}}, |
| 293 | }, { |
| 294 | appendOps: ops{appendRaw(dhex("818080808080808000"))}, |
| 295 | consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 9}}, |
| 296 | }, { |
| 297 | appendOps: ops{appendRaw(dhex("81808080808080808000"))}, |
| 298 | consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 10}}, |
| 299 | }, { |
| 300 | appendOps: ops{appendRaw(dhex("8180808080808080808000"))}, |
| 301 | consumeOps: ops{consumeVarint{wantErr: errOverflow}}, |
| 302 | }}) |
| 303 | } |
| 304 | |
| 305 | func TestFixed32(t *testing.T) { |
| 306 | runTests(t, []testOps{{ |
| 307 | appendOps: ops{appendRaw(dhex(""))}, |
| 308 | consumeOps: ops{consumeFixed32{wantErr: io.ErrUnexpectedEOF}}, |
| 309 | }, { |
| 310 | appendOps: ops{appendRaw(dhex("000000"))}, |
| 311 | consumeOps: ops{consumeFixed32{wantErr: io.ErrUnexpectedEOF}}, |
| 312 | }, { |
| 313 | appendOps: ops{appendFixed32{0}}, |
| 314 | wantRaw: dhex("00000000"), |
| 315 | consumeOps: ops{consumeFixed32{wantVal: 0, wantCnt: 4}}, |
| 316 | }, { |
| 317 | appendOps: ops{appendFixed32{math.MaxUint32}}, |
| 318 | wantRaw: dhex("ffffffff"), |
| 319 | consumeOps: ops{consumeFixed32{wantVal: math.MaxUint32, wantCnt: 4}}, |
| 320 | }, { |
| 321 | appendOps: ops{appendFixed32{0xf0e1d2c3}}, |
| 322 | wantRaw: dhex("c3d2e1f0"), |
| 323 | consumeOps: ops{consumeFixed32{wantVal: 0xf0e1d2c3, wantCnt: 4}}, |
| 324 | }}) |
| 325 | } |
| 326 | |
| 327 | func TestFixed64(t *testing.T) { |
| 328 | runTests(t, []testOps{{ |
| 329 | appendOps: ops{appendRaw(dhex(""))}, |
| 330 | consumeOps: ops{consumeFixed64{wantErr: io.ErrUnexpectedEOF}}, |
| 331 | }, { |
| 332 | appendOps: ops{appendRaw(dhex("00000000000000"))}, |
| 333 | consumeOps: ops{consumeFixed64{wantErr: io.ErrUnexpectedEOF}}, |
| 334 | }, { |
| 335 | appendOps: ops{appendFixed64{0}}, |
| 336 | wantRaw: dhex("0000000000000000"), |
| 337 | consumeOps: ops{consumeFixed64{wantVal: 0, wantCnt: 8}}, |
| 338 | }, { |
| 339 | appendOps: ops{appendFixed64{math.MaxUint64}}, |
| 340 | wantRaw: dhex("ffffffffffffffff"), |
| 341 | consumeOps: ops{consumeFixed64{wantVal: math.MaxUint64, wantCnt: 8}}, |
| 342 | }, { |
| 343 | appendOps: ops{appendFixed64{0xf0e1d2c3b4a59687}}, |
| 344 | wantRaw: dhex("8796a5b4c3d2e1f0"), |
| 345 | consumeOps: ops{consumeFixed64{wantVal: 0xf0e1d2c3b4a59687, wantCnt: 8}}, |
Joe Tsai | 22505a4 | 2018-08-01 13:12:49 -0700 | [diff] [blame] | 346 | }}) |
| 347 | } |
| 348 | |
| 349 | func TestBytes(t *testing.T) { |
| 350 | runTests(t, []testOps{{ |
Joe Tsai | be60f99 | 2018-08-01 17:16:57 -0700 | [diff] [blame^] | 351 | appendOps: ops{appendRaw(dhex(""))}, |
| 352 | consumeOps: ops{consumeBytes{wantErr: io.ErrUnexpectedEOF}}, |
| 353 | }, { |
| 354 | appendOps: ops{appendRaw(dhex("01"))}, |
| 355 | consumeOps: ops{consumeBytes{wantErr: io.ErrUnexpectedEOF}}, |
| 356 | }, { |
Joe Tsai | 22505a4 | 2018-08-01 13:12:49 -0700 | [diff] [blame] | 357 | appendOps: ops{appendVarint{0}, appendRaw("")}, |
| 358 | wantRaw: dhex("00"), |
| 359 | consumeOps: ops{consumeBytes{wantVal: dhex(""), wantCnt: 1}}, |
| 360 | }, { |
| 361 | appendOps: ops{appendBytes{[]byte("hello")}}, |
| 362 | wantRaw: []byte("\x05hello"), |
| 363 | consumeOps: ops{consumeBytes{wantVal: []byte("hello"), wantCnt: 6}}, |
| 364 | }, { |
| 365 | appendOps: ops{appendBytes{[]byte(strings.Repeat("hello", 50))}}, |
| 366 | wantRaw: []byte("\xfa\x01" + strings.Repeat("hello", 50)), |
| 367 | consumeOps: ops{consumeBytes{wantVal: []byte(strings.Repeat("hello", 50)), wantCnt: 252}}, |
| 368 | }, { |
| 369 | appendOps: ops{appendRaw("\x85\x80\x00hello")}, |
| 370 | consumeOps: ops{consumeBytes{wantVal: []byte("hello"), wantCnt: 8}}, |
| 371 | }, { |
| 372 | appendOps: ops{appendRaw("\x85\x80\x00hell")}, |
| 373 | consumeOps: ops{consumeBytes{wantErr: io.ErrUnexpectedEOF}}, |
| 374 | }}) |
| 375 | } |
| 376 | |
| 377 | func TestGroup(t *testing.T) { |
| 378 | runTests(t, []testOps{{ |
| 379 | appendOps: ops{appendRaw(dhex(""))}, |
| 380 | consumeOps: ops{consumeGroup{wantErr: io.ErrUnexpectedEOF}}, |
| 381 | }, { |
| 382 | appendOps: ops{appendTag{inNum: 0, inType: StartGroupType}}, |
| 383 | consumeOps: ops{consumeGroup{inNum: 1, wantErr: errFieldNumber}}, |
| 384 | }, { |
| 385 | appendOps: ops{appendTag{inNum: 2, inType: EndGroupType}}, |
| 386 | consumeOps: ops{consumeGroup{inNum: 1, wantErr: errEndGroup}}, |
| 387 | }, { |
| 388 | appendOps: ops{appendTag{inNum: 1, inType: EndGroupType}}, |
| 389 | consumeOps: ops{consumeGroup{inNum: 1, wantCnt: 1}}, |
| 390 | }, { |
| 391 | appendOps: ops{ |
| 392 | appendTag{inNum: 5, inType: Fixed32Type}, |
| 393 | appendFixed32{0xf0e1d2c3}, |
| 394 | appendTag{inNum: 5, inType: EndGroupType}, |
| 395 | }, |
| 396 | wantRaw: dhex("2dc3d2e1f02c"), |
| 397 | consumeOps: ops{consumeGroup{inNum: 5, wantVal: dhex("2dc3d2e1f0"), wantCnt: 6}}, |
| 398 | }, { |
| 399 | appendOps: ops{ |
| 400 | appendTag{inNum: 5, inType: Fixed32Type}, |
| 401 | appendFixed32{0xf0e1d2c3}, |
| 402 | appendRaw(dhex("ac808000")), |
| 403 | }, |
| 404 | consumeOps: ops{consumeGroup{inNum: 5, wantVal: dhex("2dc3d2e1f0"), wantCnt: 9}}, |
| 405 | }}) |
| 406 | } |
| 407 | |
| 408 | func TestField(t *testing.T) { |
| 409 | runTests(t, []testOps{{ |
| 410 | appendOps: ops{appendRaw(dhex(""))}, |
| 411 | consumeOps: ops{consumeField{wantErr: io.ErrUnexpectedEOF}}, |
| 412 | }, { |
| 413 | appendOps: ops{ |
| 414 | appendTag{inNum: 5000, inType: StartGroupType}, |
| 415 | appendTag{inNum: 1, inType: VarintType}, |
| 416 | appendVarint{123456789}, |
| 417 | appendTag{inNum: 12, inType: Fixed32Type}, |
| 418 | appendFixed32{123456789}, |
| 419 | appendTag{inNum: 123, inType: Fixed64Type}, |
| 420 | appendFixed64{123456789}, |
| 421 | appendTag{inNum: 1234, inType: BytesType}, |
| 422 | appendBytes{[]byte("hello")}, |
| 423 | appendTag{inNum: 12345, inType: StartGroupType}, |
| 424 | appendTag{inNum: 11, inType: VarintType}, |
| 425 | appendVarint{123456789}, |
| 426 | appendTag{inNum: 1212, inType: Fixed32Type}, |
| 427 | appendFixed32{123456789}, |
| 428 | appendTag{inNum: 123123, inType: Fixed64Type}, |
| 429 | appendFixed64{123456789}, |
| 430 | appendTag{inNum: 12341234, inType: BytesType}, |
| 431 | appendBytes{[]byte("goodbye")}, |
| 432 | appendTag{inNum: 12345, inType: EndGroupType}, |
| 433 | appendTag{inNum: 5000, inType: EndGroupType}, |
| 434 | }, |
| 435 | wantRaw: dhex("c3b80208959aef3a6515cd5b07d90715cd5b0700000000924d0568656c6c6fcb830658959aef3ae54b15cd5b07998f3c15cd5b070000000092ff892f07676f6f64627965cc8306c4b802"), |
| 436 | consumeOps: ops{ |
| 437 | consumeTag{wantNum: 5000, wantType: StartGroupType, wantCnt: 3}, |
| 438 | consumeTag{wantNum: 1, wantType: VarintType, wantCnt: 1}, |
| 439 | consumeVarint{wantVal: 123456789, wantCnt: 4}, |
| 440 | consumeTag{wantNum: 12, wantType: Fixed32Type, wantCnt: 1}, |
| 441 | consumeFixed32{wantVal: 123456789, wantCnt: 4}, |
| 442 | consumeTag{wantNum: 123, wantType: Fixed64Type, wantCnt: 2}, |
| 443 | consumeFixed64{wantVal: 123456789, wantCnt: 8}, |
| 444 | consumeTag{wantNum: 1234, wantType: BytesType, wantCnt: 2}, |
| 445 | consumeBytes{wantVal: []byte("hello"), wantCnt: 6}, |
| 446 | consumeTag{wantNum: 12345, wantType: StartGroupType, wantCnt: 3}, |
| 447 | consumeTag{wantNum: 11, wantType: VarintType, wantCnt: 1}, |
| 448 | consumeVarint{wantVal: 123456789, wantCnt: 4}, |
| 449 | consumeTag{wantNum: 1212, wantType: Fixed32Type, wantCnt: 2}, |
| 450 | consumeFixed32{wantVal: 123456789, wantCnt: 4}, |
| 451 | consumeTag{wantNum: 123123, wantType: Fixed64Type, wantCnt: 3}, |
| 452 | consumeFixed64{wantVal: 123456789, wantCnt: 8}, |
| 453 | consumeTag{wantNum: 12341234, wantType: BytesType, wantCnt: 4}, |
| 454 | consumeBytes{wantVal: []byte("goodbye"), wantCnt: 8}, |
| 455 | consumeTag{wantNum: 12345, wantType: EndGroupType, wantCnt: 3}, |
| 456 | consumeTag{wantNum: 5000, wantType: EndGroupType, wantCnt: 3}, |
| 457 | }, |
| 458 | }, { |
| 459 | appendOps: ops{appendRaw(dhex("c3b80208959aef3a6515cd5b07d90715cd5b0700000000924d0568656c6c6fcb830658959aef3ae54b15cd5b07998f3c15cd5b070000000092ff892f07676f6f64627965cc8306c4b802"))}, |
| 460 | consumeOps: ops{consumeField{wantNum: 5000, wantType: StartGroupType, wantCnt: 74}}, |
| 461 | }, { |
| 462 | appendOps: ops{appendTag{inNum: 5, inType: EndGroupType}}, |
| 463 | wantRaw: dhex("2c"), |
| 464 | consumeOps: ops{consumeField{wantErr: errEndGroup}}, |
| 465 | }, { |
| 466 | appendOps: ops{ |
| 467 | appendTag{inNum: 1, inType: StartGroupType}, |
| 468 | appendTag{inNum: 22, inType: StartGroupType}, |
| 469 | appendTag{inNum: 333, inType: StartGroupType}, |
| 470 | appendTag{inNum: 4444, inType: StartGroupType}, |
| 471 | appendTag{inNum: 4444, inType: EndGroupType}, |
| 472 | appendTag{inNum: 333, inType: EndGroupType}, |
| 473 | appendTag{inNum: 22, inType: EndGroupType}, |
| 474 | appendTag{inNum: 1, inType: EndGroupType}, |
| 475 | }, |
| 476 | wantRaw: dhex("0bb301eb14e39502e49502ec14b4010c"), |
| 477 | consumeOps: ops{consumeField{wantNum: 1, wantType: StartGroupType, wantCnt: 16}}, |
| 478 | }, { |
| 479 | appendOps: ops{ |
| 480 | appendTag{inNum: 1, inType: StartGroupType}, |
| 481 | appendGroup{inNum: 1, inVal: dhex("b301eb14e39502e49502ec14b401")}, |
| 482 | }, |
| 483 | wantRaw: dhex("0b" + "b301eb14e39502e49502ec14b401" + "0c"), |
| 484 | consumeOps: ops{consumeField{wantNum: 1, wantType: StartGroupType, wantCnt: 16}}, |
| 485 | }, { |
| 486 | appendOps: ops{ |
| 487 | appendTag{inNum: 1, inType: StartGroupType}, |
| 488 | appendTag{inNum: 22, inType: StartGroupType}, |
| 489 | appendTag{inNum: 333, inType: StartGroupType}, |
| 490 | appendTag{inNum: 4444, inType: StartGroupType}, |
| 491 | appendTag{inNum: 333, inType: EndGroupType}, |
| 492 | appendTag{inNum: 22, inType: EndGroupType}, |
| 493 | appendTag{inNum: 1, inType: EndGroupType}, |
| 494 | }, |
| 495 | consumeOps: ops{consumeField{wantErr: errEndGroup}}, |
| 496 | }, { |
| 497 | appendOps: ops{ |
| 498 | appendTag{inNum: 1, inType: StartGroupType}, |
| 499 | appendTag{inNum: 22, inType: StartGroupType}, |
| 500 | appendTag{inNum: 333, inType: StartGroupType}, |
| 501 | appendTag{inNum: 4444, inType: StartGroupType}, |
| 502 | appendTag{inNum: 4444, inType: EndGroupType}, |
| 503 | appendTag{inNum: 333, inType: EndGroupType}, |
| 504 | appendTag{inNum: 22, inType: EndGroupType}, |
| 505 | }, |
| 506 | consumeOps: ops{consumeField{wantErr: io.ErrUnexpectedEOF}}, |
| 507 | }, { |
| 508 | appendOps: ops{ |
| 509 | appendTag{inNum: 1, inType: StartGroupType}, |
| 510 | appendTag{inNum: 22, inType: StartGroupType}, |
| 511 | appendTag{inNum: 333, inType: StartGroupType}, |
| 512 | appendTag{inNum: 4444, inType: StartGroupType}, |
| 513 | appendTag{inNum: 0, inType: VarintType}, |
| 514 | appendTag{inNum: 4444, inType: EndGroupType}, |
| 515 | appendTag{inNum: 333, inType: EndGroupType}, |
| 516 | appendTag{inNum: 22, inType: EndGroupType}, |
| 517 | appendTag{inNum: 1, inType: EndGroupType}, |
| 518 | }, |
| 519 | consumeOps: ops{consumeField{wantErr: errFieldNumber}}, |
| 520 | }, { |
| 521 | appendOps: ops{ |
| 522 | appendTag{inNum: 1, inType: StartGroupType}, |
| 523 | appendTag{inNum: 22, inType: StartGroupType}, |
| 524 | appendTag{inNum: 333, inType: StartGroupType}, |
| 525 | appendTag{inNum: 4444, inType: StartGroupType}, |
| 526 | appendTag{inNum: 1, inType: 6}, |
| 527 | appendTag{inNum: 4444, inType: EndGroupType}, |
| 528 | appendTag{inNum: 333, inType: EndGroupType}, |
| 529 | appendTag{inNum: 22, inType: EndGroupType}, |
| 530 | appendTag{inNum: 1, inType: EndGroupType}, |
| 531 | }, |
| 532 | consumeOps: ops{consumeField{wantErr: errReserved}}, |
| 533 | }}) |
| 534 | } |
| 535 | |
| 536 | func runTests(t *testing.T, tests []testOps) { |
| 537 | for _, tt := range tests { |
| 538 | t.Run("", func(t *testing.T) { |
| 539 | var b []byte |
| 540 | for _, op := range tt.appendOps { |
| 541 | b0 := b |
| 542 | switch op := op.(type) { |
| 543 | case appendTag: |
| 544 | b = AppendTag(b, op.inNum, op.inType) |
| 545 | case appendVarint: |
| 546 | b = AppendVarint(b, op.inVal) |
| 547 | case appendFixed32: |
| 548 | b = AppendFixed32(b, op.inVal) |
| 549 | case appendFixed64: |
| 550 | b = AppendFixed64(b, op.inVal) |
| 551 | case appendBytes: |
| 552 | b = AppendBytes(b, op.inVal) |
| 553 | case appendGroup: |
| 554 | b = AppendGroup(b, op.inNum, op.inVal) |
| 555 | case appendRaw: |
| 556 | b = append(b, op...) |
| 557 | } |
| 558 | |
| 559 | check := func(label string, want int) { |
| 560 | t.Helper() |
| 561 | if got := len(b) - len(b0); got != want { |
| 562 | t.Errorf("len(Append%v) and Size%v mismatch: got %v, want %v", label, label, got, want) |
| 563 | } |
| 564 | } |
| 565 | switch op := op.(type) { |
| 566 | case appendTag: |
| 567 | check("Tag", SizeTag(op.inNum)) |
| 568 | case appendVarint: |
| 569 | check("Varint", SizeVarint(op.inVal)) |
| 570 | case appendFixed32: |
| 571 | check("Fixed32", SizeFixed32()) |
| 572 | case appendFixed64: |
| 573 | check("Fixed64", SizeFixed64()) |
| 574 | case appendBytes: |
| 575 | check("Bytes", SizeBytes(len(op.inVal))) |
| 576 | case appendGroup: |
| 577 | check("Group", SizeGroup(op.inNum, len(op.inVal))) |
| 578 | } |
| 579 | } |
| 580 | |
| 581 | if tt.wantRaw != nil && !bytes.Equal(b, tt.wantRaw) { |
| 582 | t.Errorf("raw output mismatch:\ngot %x\nwant %x", b, tt.wantRaw) |
| 583 | } |
| 584 | |
| 585 | for _, op := range tt.consumeOps { |
| 586 | check := func(label string, gotCnt, wantCnt int, wantErr error) { |
| 587 | t.Helper() |
| 588 | gotErr := ParseError(gotCnt) |
| 589 | if gotCnt < 0 { |
| 590 | gotCnt = 0 |
| 591 | } |
| 592 | if gotCnt != wantCnt { |
| 593 | t.Errorf("Consume%v(): consumed %d bytes, want %d bytes consumed", label, gotCnt, wantCnt) |
| 594 | } |
| 595 | if gotErr != wantErr { |
| 596 | t.Errorf("Consume%v(): got %v error, want %v error", label, gotErr, wantErr) |
| 597 | } |
| 598 | b = b[gotCnt:] |
| 599 | } |
| 600 | switch op := op.(type) { |
| 601 | case consumeField: |
| 602 | gotNum, gotType, n := ConsumeField(b) |
| 603 | if gotNum != op.wantNum || gotType != op.wantType { |
| 604 | t.Errorf("ConsumeField() = (%d, %v), want (%d, %v)", gotNum, gotType, op.wantNum, op.wantType) |
| 605 | } |
| 606 | check("Field", n, op.wantCnt, op.wantErr) |
| 607 | case consumeFieldValue: |
| 608 | n := ConsumeFieldValue(op.inNum, op.inType, b) |
| 609 | check("FieldValue", n, op.wantCnt, op.wantErr) |
| 610 | case consumeTag: |
| 611 | gotNum, gotType, n := ConsumeTag(b) |
| 612 | if gotNum != op.wantNum || gotType != op.wantType { |
| 613 | t.Errorf("ConsumeTag() = (%d, %v), want (%d, %v)", gotNum, gotType, op.wantNum, op.wantType) |
| 614 | } |
| 615 | check("Tag", n, op.wantCnt, op.wantErr) |
| 616 | case consumeVarint: |
| 617 | gotVal, n := ConsumeVarint(b) |
| 618 | if gotVal != op.wantVal { |
| 619 | t.Errorf("ConsumeVarint() = %d, want %d", gotVal, op.wantVal) |
| 620 | } |
| 621 | check("Varint", n, op.wantCnt, op.wantErr) |
| 622 | case consumeFixed32: |
| 623 | gotVal, n := ConsumeFixed32(b) |
| 624 | if gotVal != op.wantVal { |
| 625 | t.Errorf("ConsumeFixed32() = %d, want %d", gotVal, op.wantVal) |
| 626 | } |
| 627 | check("Fixed32", n, op.wantCnt, op.wantErr) |
| 628 | case consumeFixed64: |
| 629 | gotVal, n := ConsumeFixed64(b) |
| 630 | if gotVal != op.wantVal { |
| 631 | t.Errorf("ConsumeFixed64() = %d, want %d", gotVal, op.wantVal) |
| 632 | } |
| 633 | check("Fixed64", n, op.wantCnt, op.wantErr) |
| 634 | case consumeBytes: |
| 635 | gotVal, n := ConsumeBytes(b) |
| 636 | if !bytes.Equal(gotVal, op.wantVal) { |
| 637 | t.Errorf("ConsumeBytes() = %x, want %x", gotVal, op.wantVal) |
| 638 | } |
| 639 | check("Bytes", n, op.wantCnt, op.wantErr) |
| 640 | case consumeGroup: |
| 641 | gotVal, n := ConsumeGroup(op.inNum, b) |
| 642 | if !bytes.Equal(gotVal, op.wantVal) { |
| 643 | t.Errorf("ConsumeGroup() = %x, want %x", gotVal, op.wantVal) |
| 644 | } |
| 645 | check("Group", n, op.wantCnt, op.wantErr) |
| 646 | } |
| 647 | } |
| 648 | }) |
| 649 | } |
| 650 | } |
| 651 | |
| 652 | func TestZigZag(t *testing.T) { |
| 653 | tests := []struct { |
| 654 | dec int64 |
| 655 | enc uint64 |
| 656 | }{ |
| 657 | {math.MinInt64 + 0, math.MaxUint64 - 0}, |
| 658 | {math.MinInt64 + 1, math.MaxUint64 - 2}, |
| 659 | {math.MinInt64 + 2, math.MaxUint64 - 4}, |
| 660 | {-3, 5}, |
| 661 | {-2, 3}, |
| 662 | {-1, 1}, |
| 663 | {0, 0}, |
| 664 | {+1, 2}, |
| 665 | {+2, 4}, |
| 666 | {+3, 6}, |
| 667 | {math.MaxInt64 - 2, math.MaxUint64 - 5}, |
| 668 | {math.MaxInt64 - 1, math.MaxUint64 - 3}, |
| 669 | {math.MaxInt64 - 0, math.MaxUint64 - 1}, |
| 670 | } |
| 671 | |
| 672 | for _, tt := range tests { |
| 673 | if enc := EncodeZigZag(tt.dec); enc != tt.enc { |
| 674 | t.Errorf("EncodeZigZag(%d) = %d, want %d", tt.dec, enc, tt.enc) |
| 675 | } |
| 676 | if dec := DecodeZigZag(tt.enc); dec != tt.dec { |
| 677 | t.Errorf("DecodeZigZag(%d) = %d, want %d", tt.enc, dec, tt.dec) |
| 678 | } |
| 679 | } |
| 680 | } |