forked from OSchip/llvm-project
112 lines
3.2 KiB
C++
112 lines
3.2 KiB
C++
//===-- stack_trace_compressor.cpp ------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "gwp_asan/stack_trace_compressor.h"
|
|
|
|
namespace gwp_asan {
|
|
namespace compression {
|
|
namespace {
|
|
// Encodes `Value` as a variable-length integer to `Out`. Returns zero if there
|
|
// was not enough space in the output buffer to write the complete varInt.
|
|
// Otherwise returns the length of the encoded integer.
|
|
size_t varIntEncode(uintptr_t Value, uint8_t *Out, size_t OutLen) {
|
|
for (size_t i = 0; i < OutLen; ++i) {
|
|
Out[i] = Value & 0x7f;
|
|
Value >>= 7;
|
|
if (!Value)
|
|
return i + 1;
|
|
|
|
Out[i] |= 0x80;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Decodes a variable-length integer to `Out`. Returns zero if the integer was
|
|
// too large to be represented in a uintptr_t, or if the input buffer finished
|
|
// before the integer was decoded (either case meaning that the `In` does not
|
|
// point to a valid varInt buffer). Otherwise, returns the number of bytes that
|
|
// were used to store the decoded integer.
|
|
size_t varIntDecode(const uint8_t *In, size_t InLen, uintptr_t *Out) {
|
|
*Out = 0;
|
|
uint8_t Shift = 0;
|
|
|
|
for (size_t i = 0; i < InLen; ++i) {
|
|
*Out |= (static_cast<uintptr_t>(In[i]) & 0x7f) << Shift;
|
|
|
|
if (In[i] < 0x80)
|
|
return i + 1;
|
|
|
|
Shift += 7;
|
|
|
|
// Disallow overflowing the range of the output integer.
|
|
if (Shift >= sizeof(uintptr_t) * 8)
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uintptr_t zigzagEncode(uintptr_t Value) {
|
|
uintptr_t Encoded = Value << 1;
|
|
if (static_cast<intptr_t>(Value) >= 0)
|
|
return Encoded;
|
|
return ~Encoded;
|
|
}
|
|
|
|
uintptr_t zigzagDecode(uintptr_t Value) {
|
|
uintptr_t Decoded = Value >> 1;
|
|
if (!(Value & 1))
|
|
return Decoded;
|
|
return ~Decoded;
|
|
}
|
|
} // anonymous namespace
|
|
|
|
size_t pack(const uintptr_t *Unpacked, size_t UnpackedSize, uint8_t *Packed,
|
|
size_t PackedMaxSize) {
|
|
size_t Index = 0;
|
|
for (size_t CurrentDepth = 0; CurrentDepth < UnpackedSize; CurrentDepth++) {
|
|
uintptr_t Diff = Unpacked[CurrentDepth];
|
|
if (CurrentDepth > 0)
|
|
Diff -= Unpacked[CurrentDepth - 1];
|
|
size_t EncodedLength =
|
|
varIntEncode(zigzagEncode(Diff), Packed + Index, PackedMaxSize - Index);
|
|
if (!EncodedLength)
|
|
break;
|
|
|
|
Index += EncodedLength;
|
|
}
|
|
|
|
return Index;
|
|
}
|
|
|
|
size_t unpack(const uint8_t *Packed, size_t PackedSize, uintptr_t *Unpacked,
|
|
size_t UnpackedMaxSize) {
|
|
size_t CurrentDepth;
|
|
size_t Index = 0;
|
|
for (CurrentDepth = 0; CurrentDepth < UnpackedMaxSize; CurrentDepth++) {
|
|
uintptr_t EncodedDiff;
|
|
size_t DecodedLength =
|
|
varIntDecode(Packed + Index, PackedSize - Index, &EncodedDiff);
|
|
if (!DecodedLength)
|
|
break;
|
|
Index += DecodedLength;
|
|
|
|
Unpacked[CurrentDepth] = zigzagDecode(EncodedDiff);
|
|
if (CurrentDepth > 0)
|
|
Unpacked[CurrentDepth] += Unpacked[CurrentDepth - 1];
|
|
}
|
|
|
|
if (Index != PackedSize && CurrentDepth != UnpackedMaxSize)
|
|
return 0;
|
|
|
|
return CurrentDepth;
|
|
}
|
|
|
|
} // namespace compression
|
|
} // namespace gwp_asan
|