Files
Project64-Legacy/cbor-lite/codec.h
Jay Oster 654b8f09b1 Memory editor (#24)
* Switch hex editor to full CUSTOMDRAW

- This will allow us to draw with more precision. Especially for
  selection ranges and editing.

* Add basic hex selection

* Add ASCII column text selection

* Ctrl+C to copy selected memory

* Fix moving the window

* Fix minor bugs in memory viewer with watchpoints

- The colors and underlines were only showing when a watchpoint was
  disabled.
- If the last hex byte in a row was underlined, the entire ASCII section
  would also be underlined on that row.
- Now colors always show when a watchpoint exists. And the byte is
  underlined only when the watchpoint is enabled.

* Fix copying the last line of memory with some selection sizes

- This bug prevented the last line of the memory view from being copied
  to the clipboard when the start of the selection was at a higher
  column number than the end of the selection.
- E.g. when address range 0x8000000c-0x80000023 was selected, it would
  only copy 0x8000000c-0x8000001f, truncating the last line and missing
  four selected bytes.

* Add memory editor bookmarks

* Add missing session files to VS project filters
2023-08-19 16:37:21 +02:00

498 lines
20 KiB
C++

#pragma once
// This file is part of CBOR-lite which is copyright Isode Limited
// and others and released under a MIT license. For details, see the
// COPYRIGHT.md file in the top-level folder of the CBOR-lite software
// distribution.
#include <exception>
#include <iterator>
#include <string>
#include <type_traits>
namespace CborLite {
class Exception : public std::exception {
public:
Exception() noexcept {
}
virtual ~Exception() noexcept = default;
Exception(const char *d) noexcept {
what_ += std::string(": ") + d;
}
Exception(const std::string &d) noexcept {
what_ += ": " + d;
}
Exception(const Exception &e) noexcept : what_(e.what_) {
}
Exception(Exception &&e) noexcept : what_(std::move(e.what_)) {
// Note that e.what_ is not re-initialized to "CBOR Exception" as
// the moved-from object is not expected to ever be reused.
}
Exception &operator=(const Exception &) = delete;
Exception &operator=(Exception &&) = delete;
virtual const char *what() const noexcept {
return what_.c_str();
}
private:
std::string what_ = "CBOR Exception";
};
using Tag = std::uint_fast64_t;
namespace Major {
constexpr Tag unsignedInteger = 0u;
constexpr Tag negativeInteger = 1u << 5;
constexpr Tag byteString = 2u << 5;
constexpr Tag textString = 3u << 5;
constexpr Tag array = 4u << 5;
constexpr Tag map = 5u << 5;
constexpr Tag semantic = 6u << 5;
constexpr Tag floatingPoint = 7u << 5;
constexpr Tag simple = 7u << 5;
constexpr Tag mask = 0xe0u;
} // namespace Major
namespace Minor {
constexpr Tag length1 = 24u;
constexpr Tag length2 = 25u;
constexpr Tag length4 = 26u;
constexpr Tag length8 = 27u;
constexpr Tag False = 20u;
constexpr Tag True = 21u;
constexpr Tag null = 22u;
constexpr Tag undefined = 23u;
constexpr Tag halfFloat = 25u; // not implemented
constexpr Tag singleFloat = 26u;
constexpr Tag doubleFloat = 27u;
constexpr Tag dataTime = 0u;
constexpr Tag epochDataTime = 1u;
constexpr Tag positiveBignum = 2u;
constexpr Tag negativeBignum = 3u;
constexpr Tag decimalFraction = 4u;
constexpr Tag bigfloat = 5u;
constexpr Tag convertBase64Url = 21u;
constexpr Tag convertBase64 = 22u;
constexpr Tag convertBase16 = 23u;
constexpr Tag cborEncodedData = 24u;
constexpr Tag uri = 32u;
constexpr Tag base64Url = 33u;
constexpr Tag base64 = 34u;
constexpr Tag regex = 35u;
constexpr Tag mimeMessage = 36u;
constexpr Tag selfDescribeCbor = 55799u;
constexpr Tag mask = 0x1fu;
} // namespace Minor
constexpr Tag undefined = Major::semantic + Minor::undefined;
using Flags = unsigned;
namespace Flag {
constexpr Flags none = 0;
constexpr Flags requireMinimalEncoding = 1 << 0;
} // namespace Flag
template <typename Type>
typename std::enable_if<std::is_unsigned<Type>::value, std::size_t>::type length(Type val) {
if (val < 24) return 0;
for (std::size_t i = 1; i <= ((sizeof val) >> 1); i <<= 1) {
if (!(val >> (i << 3))) return i;
}
return sizeof val;
}
template <typename Buffer>
typename std::enable_if<std::is_class<Buffer>::value, std::size_t>::type encodeTagAndAdditional(
Buffer &buffer, Tag tag, Tag additional) {
buffer.push_back(static_cast<char>(tag + additional));
return 1;
}
template <typename InputIterator>
typename std::enable_if<std::is_class<InputIterator>::value, std::size_t>::type decodeTagAndAdditional(
InputIterator &pos, InputIterator end, Tag &tag, Tag &additional, Flags = Flag::none) {
if (pos == end) throw Exception("not enough input");
auto octet = *(pos++);
tag = octet & Major::mask;
additional = octet & Minor::mask;
return 1;
}
// PJ64: Added type specialization for 64-bit values
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value && std::is_unsigned<Type>::value && sizeof(Type) == 8, std::size_t>::type encodeTagAndValue(
Buffer &buffer, Tag tag, const Type t) {
auto len = length(t);
buffer.reserve(buffer.size() + len + 1);
switch (len) {
case 8:
encodeTagAndAdditional(buffer, tag, Minor::length8);
break;
case 4:
encodeTagAndAdditional(buffer, tag, Minor::length4);
break;
case 2:
encodeTagAndAdditional(buffer, tag, Minor::length2);
break;
case 1:
encodeTagAndAdditional(buffer, tag, Minor::length1);
break;
case 0:
return encodeTagAndAdditional(buffer, tag, t);
default:
throw Exception("too long");
}
switch (len) {
case 8:
buffer.push_back((t >> 56) & 0xffU);
buffer.push_back((t >> 48) & 0xffU);
buffer.push_back((t >> 40) & 0xffU);
buffer.push_back((t >> 32) & 0xffU);
case 4:
buffer.push_back((t >> 24) & 0xffU);
buffer.push_back((t >> 16) & 0xffU);
case 2:
buffer.push_back((t >> 8) & 0xffU);
case 1:
buffer.push_back(t & 0xffU);
}
return 1 + len;
}
// PJ64: Added type specialization for 32-bit values
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value && std::is_unsigned<Type>::value && sizeof(Type) == 4, std::size_t>::type encodeTagAndValue(
Buffer &buffer, Tag tag, const Type t) {
auto len = length(t);
buffer.reserve(buffer.size() + len + 1);
switch (len) {
case 4:
encodeTagAndAdditional(buffer, tag, Minor::length4);
break;
case 2:
encodeTagAndAdditional(buffer, tag, Minor::length2);
break;
case 1:
encodeTagAndAdditional(buffer, tag, Minor::length1);
break;
case 0:
return encodeTagAndAdditional(buffer, tag, t);
default:
throw Exception("too long");
}
switch (len) {
case 4:
buffer.push_back((t >> 24) & 0xffU);
buffer.push_back((t >> 16) & 0xffU);
case 2:
buffer.push_back((t >> 8) & 0xffU);
case 1:
buffer.push_back(t & 0xffU);
}
return 1 + len;
}
// PJ64: Added type specialization for 64-bit values
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && std::is_unsigned<Type>::value && sizeof(Type) == 8, std::size_t>::type decodeTagAndValue(
InputIterator &pos, InputIterator end, Tag &tag, Type &t, Flags flags = Flag::none) {
if (pos == end) throw Exception("not enough input");
auto additional = Minor::undefined;
auto len = decodeTagAndAdditional(pos, end, tag, additional, flags);
if (additional < Minor::length1) {
t = additional;
return len;
}
t = 0u;
switch (additional) {
case Minor::length8:
if (std::distance(pos, end) < 8) throw Exception("not enough input");
t |= static_cast<Type>(reinterpret_cast<const unsigned char &>(*(pos++))) << 56;
t |= static_cast<Type>(reinterpret_cast<const unsigned char &>(*(pos++))) << 48;
t |= static_cast<Type>(reinterpret_cast<const unsigned char &>(*(pos++))) << 40;
t |= static_cast<Type>(reinterpret_cast<const unsigned char &>(*(pos++))) << 32;
len += 4;
if ((flags & Flag::requireMinimalEncoding) && !t) throw Exception("encoding not minimal");
case Minor::length4:
if (std::distance(pos, end) < 4) throw Exception("not enough input");
t |= static_cast<Type>(reinterpret_cast<const unsigned char &>(*(pos++))) << 24;
t |= static_cast<Type>(reinterpret_cast<const unsigned char &>(*(pos++))) << 16;
len += 2;
if ((flags & Flag::requireMinimalEncoding) && !t) throw Exception("encoding not minimal");
case Minor::length2:
if (std::distance(pos, end) < 2) throw Exception("not enough input");
t |= static_cast<Type>(reinterpret_cast<const unsigned char &>(*(pos++))) << 8;
len++;
if ((flags & Flag::requireMinimalEncoding) && !t) throw Exception("encoding not minimal");
case Minor::length1:
if (std::distance(pos, end) < 1) throw Exception("not enough input");
t |= static_cast<Type>(reinterpret_cast<const unsigned char &>(*(pos++)));
len++;
if ((flags & Flag::requireMinimalEncoding) && t < 24) throw Exception("encoding not minimal");
return len;
}
throw Exception("bad additional value");
}
// PJ64: Added type specialization for 32-bit values
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && std::is_unsigned<Type>::value && sizeof(Type) == 4, std::size_t>::type decodeTagAndValue(
InputIterator &pos, InputIterator end, Tag &tag, Type &t, Flags flags = Flag::none) {
if (pos == end) throw Exception("not enough input");
auto additional = Minor::undefined;
auto len = decodeTagAndAdditional(pos, end, tag, additional, flags);
if (additional < Minor::length1) {
t = additional;
return len;
}
t = 0u;
switch (additional) {
case Minor::length4:
if (std::distance(pos, end) < 4) throw Exception("not enough input");
t |= static_cast<Type>(reinterpret_cast<const unsigned char &>(*(pos++))) << 24;
t |= static_cast<Type>(reinterpret_cast<const unsigned char &>(*(pos++))) << 16;
len += 2;
if ((flags & Flag::requireMinimalEncoding) && !t) throw Exception("encoding not minimal");
case Minor::length2:
if (std::distance(pos, end) < 2) throw Exception("not enough input");
t |= static_cast<Type>(reinterpret_cast<const unsigned char &>(*(pos++))) << 8;
len++;
if ((flags & Flag::requireMinimalEncoding) && !t) throw Exception("encoding not minimal");
case Minor::length1:
if (std::distance(pos, end) < 1) throw Exception("not enough input");
t |= static_cast<Type>(reinterpret_cast<const unsigned char &>(*(pos++)));
len++;
if ((flags & Flag::requireMinimalEncoding) && t < 24) throw Exception("encoding not minimal");
return len;
}
throw Exception("bad additional value");
}
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value, std::size_t>::type encodeUnsigned(Buffer &buffer, const Type &t) {
return encodeTagAndValue(buffer, Major::unsignedInteger, t);
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value &&std::is_unsigned<Type>::value && !std::is_const<Type>::value,
std::size_t>::type
decodeUnsigned(InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
auto tag = undefined;
auto len = decodeTagAndValue(pos, end, tag, t, flags);
if (tag != Major::unsignedInteger) throw Exception("not Unsigned");
return len;
}
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value, std::size_t>::type encodeNegative(Buffer &buffer, const Type &t) {
return encodeTagAndValue(buffer, Major::negativeInteger, t);
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value &&std::is_unsigned<Type>::value && !std::is_const<Type>::value,
std::size_t>::type
decodeNegative(InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
auto tag = undefined;
auto len = decodeTagAndValue(pos, end, tag, t, flags);
if (tag != Major::negativeInteger) throw Exception("not Unsigned");
return len;
}
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value, std::size_t>::type encodeInteger(Buffer &buffer, const Type &t) {
if (t >= 0) {
unsigned long long val = t;
return encodeUnsigned(buffer, val);
} else {
unsigned long long val = -t - 1;
return encodeNegative(buffer, val);
}
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value &&std::is_signed<Type>::value && !std::is_const<Type>::value,
std::size_t>::type
decodeInteger(InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
auto tag = undefined;
unsigned long long val;
auto len = decodeTagAndValue(pos, end, tag, val, flags);
switch (tag) {
case Major::unsignedInteger:
t = val;
break;
case Major::negativeInteger:
t = -1 - static_cast<long long>(val);
break;
default:
throw Exception("not integer");
}
return len;
}
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value &&std::is_same<bool, Type>::value, std::size_t>::type encodeBool(
Buffer &buffer, const Type &t) {
return encodeTagAndAdditional(buffer, Major::simple, t ? Minor::True : Minor::False);
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value &&std::is_same<bool, Type>::value && !std::is_const<Type>::value,
std::size_t>::type
decodeBool(InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
auto tag = undefined;
auto value = undefined;
auto len = decodeTagAndValue(pos, end, tag, value, flags);
if (tag == Major::simple) {
if (value == Minor::True) {
t = true;
return len;
} else if (value == Minor::False) {
t = false;
return len;
}
throw Exception("not Boolean");
}
throw Exception("not Simple");
}
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value, std::size_t>::type encodeBytes(Buffer &buffer, const Type &t) {
auto len = encodeTagAndValue(buffer, Major::byteString, t.size());
buffer.insert(std::end(buffer), std::begin(t), std::end(t));
return len + t.size();
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && !std::is_const<Type>::value, std::size_t>::type decodeBytes(
InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
auto tag = undefined;
auto value = undefined;
auto len = decodeTagAndValue(pos, end, tag, value, flags);
if (tag != Major::byteString) throw Exception("not ByteString");
auto dist = std::distance(pos, end);
if (dist < static_cast<decltype(dist)>(value)) throw Exception("not enough input");
t.insert(std::end(t), pos, pos + value);
std::advance(pos, value);
return len + value;
}
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value &&std::is_unsigned<Type>::value, std::size_t>::type encodeEncodedBytesPrefix(
Buffer &buffer, const Type &t) {
auto len = encodeTagAndValue(buffer, Major::semantic, Minor::cborEncodedData);
return len + encodeTagAndValue(buffer, Major::byteString, t);
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && !std::is_const<Type>::value, std::size_t>::type
decodeEncodedBytesPrefix(InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
auto tag = undefined;
auto value = undefined;
auto len = decodeTagAndValue(pos, end, tag, value, flags);
if (tag != Major::semantic || value != Minor::cborEncodedData) {
throw Exception("not CBOR Encoded Data");
}
tag = undefined;
len += decodeTagAndValue(pos, end, tag, value, flags);
if (tag != Major::byteString) throw Exception("not ByteString");
t = value;
return len;
}
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value, std::size_t>::type encodeEncodedBytes(Buffer &buffer, const Type &t) {
auto len = encodeTagAndValue(buffer, Major::semantic, Minor::cborEncodedData);
return len + encodeBytes(buffer, t);
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && !std::is_const<Type>::value, std::size_t>::type decodeEncodedBytes(
InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
auto tag = undefined;
auto value = undefined;
auto len = decodeTagAndValue(pos, end, tag, value, flags);
if (tag != Major::semantic || value != Minor::cborEncodedData) {
throw Exception("not CBOR Encoded Data");
}
return len + decodeBytes(pos, end, t, flags);
}
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value, std::size_t>::type encodeText(Buffer &buffer, const Type &t) {
auto len = encodeTagAndValue(buffer, Major::textString, t.size());
buffer.insert(std::end(buffer), std::begin(t), std::end(t));
return len + t.size();
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && !std::is_const<Type>::value, std::size_t>::type decodeText(
InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
auto tag = undefined;
auto value = undefined;
auto len = decodeTagAndValue(pos, end, tag, value, flags);
if (tag != Major::textString) throw Exception("not TextString");
auto dist = std::distance(pos, end);
if (dist < static_cast<decltype(dist)>(value)) throw Exception("not enough input");
// PJ64: 32-bit plaform support
if (sizeof(size_t) == 4 && value > UINT32_MAX) {
throw Exception("TextString too long for 32-bit platforms");
}
t.insert(std::end(t), pos, pos + (size_t)value);
std::advance(pos, (size_t)value);
return (size_t)(len + value);
}
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value &&std::is_unsigned<Type>::value, std::size_t>::type encodeArraySize(
Buffer &buffer, const Type &t) {
return encodeTagAndValue(buffer, Major::array, t);
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && !std::is_const<Type>::value &&std::is_unsigned<Type>::value,
std::size_t>::type
decodeArraySize(InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
auto tag = undefined;
auto value = undefined;
auto len = decodeTagAndValue(pos, end, tag, value, flags);
if (tag != Major::array) throw Exception("not Array");
t = value;
return len;
}
template <typename Buffer, typename Type>
typename std::enable_if<std::is_class<Buffer>::value &&std::is_unsigned<Type>::value, std::size_t>::type encodeMapSize(
Buffer &buffer, const Type &t) {
return encodeTagAndValue(buffer, Major::map, t);
}
template <typename InputIterator, typename Type>
typename std::enable_if<std::is_class<InputIterator>::value && !std::is_const<Type>::value &&std::is_unsigned<Type>::value,
std::size_t>::type
decodeMapSize(InputIterator &pos, InputIterator end, Type &t, Flags flags = Flag::none) {
auto tag = undefined;
auto value = undefined;
auto len = decodeTagAndValue(pos, end, tag, value, flags);
if (tag != Major::map) throw Exception("not Map");
t = value;
return len;
}
} // namespace CborLite