1*3cab2bb3Spatrick //===-- stack_trace_compressor.cpp ------------------------------*- C++ -*-===//
2*3cab2bb3Spatrick //
3*3cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*3cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
5*3cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*3cab2bb3Spatrick //
7*3cab2bb3Spatrick //===----------------------------------------------------------------------===//
8*3cab2bb3Spatrick
9*3cab2bb3Spatrick #include "gwp_asan/stack_trace_compressor.h"
10*3cab2bb3Spatrick
11*3cab2bb3Spatrick namespace gwp_asan {
12*3cab2bb3Spatrick namespace compression {
13*3cab2bb3Spatrick namespace {
14*3cab2bb3Spatrick // Encodes `Value` as a variable-length integer to `Out`. Returns zero if there
15*3cab2bb3Spatrick // was not enough space in the output buffer to write the complete varInt.
16*3cab2bb3Spatrick // Otherwise returns the length of the encoded integer.
varIntEncode(uintptr_t Value,uint8_t * Out,size_t OutLen)17*3cab2bb3Spatrick size_t varIntEncode(uintptr_t Value, uint8_t *Out, size_t OutLen) {
18*3cab2bb3Spatrick for (size_t i = 0; i < OutLen; ++i) {
19*3cab2bb3Spatrick Out[i] = Value & 0x7f;
20*3cab2bb3Spatrick Value >>= 7;
21*3cab2bb3Spatrick if (!Value)
22*3cab2bb3Spatrick return i + 1;
23*3cab2bb3Spatrick
24*3cab2bb3Spatrick Out[i] |= 0x80;
25*3cab2bb3Spatrick }
26*3cab2bb3Spatrick
27*3cab2bb3Spatrick return 0;
28*3cab2bb3Spatrick }
29*3cab2bb3Spatrick
30*3cab2bb3Spatrick // Decodes a variable-length integer to `Out`. Returns zero if the integer was
31*3cab2bb3Spatrick // too large to be represented in a uintptr_t, or if the input buffer finished
32*3cab2bb3Spatrick // before the integer was decoded (either case meaning that the `In` does not
33*3cab2bb3Spatrick // point to a valid varInt buffer). Otherwise, returns the number of bytes that
34*3cab2bb3Spatrick // were used to store the decoded integer.
varIntDecode(const uint8_t * In,size_t InLen,uintptr_t * Out)35*3cab2bb3Spatrick size_t varIntDecode(const uint8_t *In, size_t InLen, uintptr_t *Out) {
36*3cab2bb3Spatrick *Out = 0;
37*3cab2bb3Spatrick uint8_t Shift = 0;
38*3cab2bb3Spatrick
39*3cab2bb3Spatrick for (size_t i = 0; i < InLen; ++i) {
40*3cab2bb3Spatrick *Out |= (static_cast<uintptr_t>(In[i]) & 0x7f) << Shift;
41*3cab2bb3Spatrick
42*3cab2bb3Spatrick if (In[i] < 0x80)
43*3cab2bb3Spatrick return i + 1;
44*3cab2bb3Spatrick
45*3cab2bb3Spatrick Shift += 7;
46*3cab2bb3Spatrick
47*3cab2bb3Spatrick // Disallow overflowing the range of the output integer.
48*3cab2bb3Spatrick if (Shift >= sizeof(uintptr_t) * 8)
49*3cab2bb3Spatrick return 0;
50*3cab2bb3Spatrick }
51*3cab2bb3Spatrick return 0;
52*3cab2bb3Spatrick }
53*3cab2bb3Spatrick
zigzagEncode(uintptr_t Value)54*3cab2bb3Spatrick uintptr_t zigzagEncode(uintptr_t Value) {
55*3cab2bb3Spatrick uintptr_t Encoded = Value << 1;
56*3cab2bb3Spatrick if (static_cast<intptr_t>(Value) >= 0)
57*3cab2bb3Spatrick return Encoded;
58*3cab2bb3Spatrick return ~Encoded;
59*3cab2bb3Spatrick }
60*3cab2bb3Spatrick
zigzagDecode(uintptr_t Value)61*3cab2bb3Spatrick uintptr_t zigzagDecode(uintptr_t Value) {
62*3cab2bb3Spatrick uintptr_t Decoded = Value >> 1;
63*3cab2bb3Spatrick if (!(Value & 1))
64*3cab2bb3Spatrick return Decoded;
65*3cab2bb3Spatrick return ~Decoded;
66*3cab2bb3Spatrick }
67*3cab2bb3Spatrick } // anonymous namespace
68*3cab2bb3Spatrick
pack(const uintptr_t * Unpacked,size_t UnpackedSize,uint8_t * Packed,size_t PackedMaxSize)69*3cab2bb3Spatrick size_t pack(const uintptr_t *Unpacked, size_t UnpackedSize, uint8_t *Packed,
70*3cab2bb3Spatrick size_t PackedMaxSize) {
71*3cab2bb3Spatrick size_t Index = 0;
72*3cab2bb3Spatrick for (size_t CurrentDepth = 0; CurrentDepth < UnpackedSize; CurrentDepth++) {
73*3cab2bb3Spatrick uintptr_t Diff = Unpacked[CurrentDepth];
74*3cab2bb3Spatrick if (CurrentDepth > 0)
75*3cab2bb3Spatrick Diff -= Unpacked[CurrentDepth - 1];
76*3cab2bb3Spatrick size_t EncodedLength =
77*3cab2bb3Spatrick varIntEncode(zigzagEncode(Diff), Packed + Index, PackedMaxSize - Index);
78*3cab2bb3Spatrick if (!EncodedLength)
79*3cab2bb3Spatrick break;
80*3cab2bb3Spatrick
81*3cab2bb3Spatrick Index += EncodedLength;
82*3cab2bb3Spatrick }
83*3cab2bb3Spatrick
84*3cab2bb3Spatrick return Index;
85*3cab2bb3Spatrick }
86*3cab2bb3Spatrick
unpack(const uint8_t * Packed,size_t PackedSize,uintptr_t * Unpacked,size_t UnpackedMaxSize)87*3cab2bb3Spatrick size_t unpack(const uint8_t *Packed, size_t PackedSize, uintptr_t *Unpacked,
88*3cab2bb3Spatrick size_t UnpackedMaxSize) {
89*3cab2bb3Spatrick size_t CurrentDepth;
90*3cab2bb3Spatrick size_t Index = 0;
91*3cab2bb3Spatrick for (CurrentDepth = 0; CurrentDepth < UnpackedMaxSize; CurrentDepth++) {
92*3cab2bb3Spatrick uintptr_t EncodedDiff;
93*3cab2bb3Spatrick size_t DecodedLength =
94*3cab2bb3Spatrick varIntDecode(Packed + Index, PackedSize - Index, &EncodedDiff);
95*3cab2bb3Spatrick if (!DecodedLength)
96*3cab2bb3Spatrick break;
97*3cab2bb3Spatrick Index += DecodedLength;
98*3cab2bb3Spatrick
99*3cab2bb3Spatrick Unpacked[CurrentDepth] = zigzagDecode(EncodedDiff);
100*3cab2bb3Spatrick if (CurrentDepth > 0)
101*3cab2bb3Spatrick Unpacked[CurrentDepth] += Unpacked[CurrentDepth - 1];
102*3cab2bb3Spatrick }
103*3cab2bb3Spatrick
104*3cab2bb3Spatrick if (Index != PackedSize && CurrentDepth != UnpackedMaxSize)
105*3cab2bb3Spatrick return 0;
106*3cab2bb3Spatrick
107*3cab2bb3Spatrick return CurrentDepth;
108*3cab2bb3Spatrick }
109*3cab2bb3Spatrick
110*3cab2bb3Spatrick } // namespace compression
111*3cab2bb3Spatrick } // namespace gwp_asan
112