aboutsummaryrefslogtreecommitdiff
path: root/pw_varint/varint.cc
blob: 3d0e759d707e903d4083ca279c965ad2685fbf1b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// Copyright 2019 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

#include "pw_varint/varint.h"

#include <algorithm>

namespace pw::varint {
namespace {

constexpr int64_t ZigZagDecode64(uint64_t n) {
  return static_cast<int64_t>((n >> 1) ^ (~(n & 1) + 1));
}

}  // namespace

size_t EncodeLittleEndianBase128(uint64_t integer,
                                 const span<uint8_t>& output) {
  size_t written = 0;
  do {
    if (written >= output.size()) {
      return 0;
    }

    // Grab 7 bits; the eighth bit is set to 1 to indicate more data coming.
    output[written++] = static_cast<uint8_t>(integer) | '\x80';
    integer >>= 7;
  } while (integer != 0u);

  output[written - 1] &= '\x7f';  // clear the top bit of the last byte
  return written;
}

size_t DecodeVarint(const span<const uint8_t>& input, int64_t* value) {
  const size_t bytes = DecodeVarint(input, reinterpret_cast<uint64_t*>(value));
  *value = ZigZagDecode64(*value);
  return bytes;
}

size_t DecodeVarint(const span<const uint8_t>& input, uint64_t* value) {
  uint64_t decoded_value = 0;
  uint_fast8_t count = 0;

  // The largest 64-bit ints require 10 B.
  const size_t max_count = std::min(kMaxVarintSizeBytes, input.size());

  while (true) {
    if (count >= max_count) {
      return 0;
    }

    // Add the bottom seven bits of the next byte to the result.
    decoded_value |= static_cast<uint64_t>(input[count] & '\x7f')
                     << (7 * count);

    // Stop decoding if the top bit is not set.
    if ((input[count++] & '\x80') == 0) {
      break;
    }
  }

  *value = decoded_value;
  return count;
}

}  // namespace pw::varint