| //===-- RegisterValue.cpp ---------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "lldb/Core/RegisterValue.h" |
| |
| #include "lldb/Core/Scalar.h" |
| #include "lldb/Utility/Args.h" |
| #include "lldb/Utility/DataExtractor.h" |
| #include "lldb/Utility/Status.h" |
| #include "lldb/Utility/Stream.h" |
| #include "lldb/Utility/StreamString.h" |
| #include "lldb/lldb-defines.h" // for LLDB_INVALID_ADDRESS |
| #include "lldb/lldb-private-types.h" // for RegisterInfo, type128 |
| |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/StringRef.h" |
| |
| #include <cstdint> // for uint8_t, uint32_t, uint64_t |
| #include <string> // for string |
| #include <tuple> // for tie, tuple |
| #include <vector> |
| |
| #include <assert.h> // for assert |
| #include <inttypes.h> // for PRIx64 |
| #include <stdio.h> // for sscanf |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| bool RegisterValue::GetData(DataExtractor &data) const { |
| return data.SetData(GetBytes(), GetByteSize(), GetByteOrder()) > 0; |
| } |
| |
| uint32_t RegisterValue::GetAsMemoryData(const RegisterInfo *reg_info, void *dst, |
| uint32_t dst_len, |
| lldb::ByteOrder dst_byte_order, |
| Status &error) const { |
| if (reg_info == nullptr) { |
| error.SetErrorString("invalid register info argument."); |
| return 0; |
| } |
| |
| // ReadRegister should have already been called on this object prior to |
| // calling this. |
| if (GetType() == eTypeInvalid) { |
| // No value has been read into this object... |
| error.SetErrorStringWithFormat( |
| "invalid register value type for register %s", reg_info->name); |
| return 0; |
| } |
| |
| if (dst_len > kMaxRegisterByteSize) { |
| error.SetErrorString("destination is too big"); |
| return 0; |
| } |
| |
| const uint32_t src_len = reg_info->byte_size; |
| |
| // Extract the register data into a data extractor |
| DataExtractor reg_data; |
| if (!GetData(reg_data)) { |
| error.SetErrorString("invalid register value to copy into"); |
| return 0; |
| } |
| |
| // Prepare a memory buffer that contains some or all of the register value |
| const uint32_t bytes_copied = |
| reg_data.CopyByteOrderedData(0, // src offset |
| src_len, // src length |
| dst, // dst buffer |
| dst_len, // dst length |
| dst_byte_order); // dst byte order |
| if (bytes_copied == 0) |
| error.SetErrorStringWithFormat( |
| "failed to copy data for register write of %s", reg_info->name); |
| |
| return bytes_copied; |
| } |
| |
| uint32_t RegisterValue::SetFromMemoryData(const RegisterInfo *reg_info, |
| const void *src, uint32_t src_len, |
| lldb::ByteOrder src_byte_order, |
| Status &error) { |
| if (reg_info == nullptr) { |
| error.SetErrorString("invalid register info argument."); |
| return 0; |
| } |
| |
| // Moving from addr into a register |
| // |
| // Case 1: src_len == dst_len |
| // |
| // |AABBCCDD| Address contents |
| // |AABBCCDD| Register contents |
| // |
| // Case 2: src_len > dst_len |
| // |
| // Status! (The register should always be big enough to hold the data) |
| // |
| // Case 3: src_len < dst_len |
| // |
| // |AABB| Address contents |
| // |AABB0000| Register contents [on little-endian hardware] |
| // |0000AABB| Register contents [on big-endian hardware] |
| if (src_len > kMaxRegisterByteSize) { |
| error.SetErrorStringWithFormat( |
| "register buffer is too small to receive %u bytes of data.", src_len); |
| return 0; |
| } |
| |
| const uint32_t dst_len = reg_info->byte_size; |
| |
| if (src_len > dst_len) { |
| error.SetErrorStringWithFormat( |
| "%u bytes is too big to store in register %s (%u bytes)", src_len, |
| reg_info->name, dst_len); |
| return 0; |
| } |
| |
| // Use a data extractor to correctly copy and pad the bytes read into the |
| // register value |
| DataExtractor src_data(src, src_len, src_byte_order, 4); |
| |
| error = SetValueFromData(reg_info, src_data, 0, true); |
| if (error.Fail()) |
| return 0; |
| |
| // If SetValueFromData succeeded, we must have copied all of src_len |
| return src_len; |
| } |
| |
| bool RegisterValue::GetScalarValue(Scalar &scalar) const { |
| switch (m_type) { |
| case eTypeInvalid: |
| break; |
| case eTypeBytes: { |
| switch (buffer.length) { |
| default: |
| break; |
| case 1: |
| scalar = *(const uint8_t *)buffer.bytes; |
| return true; |
| case 2: |
| scalar = *(const uint16_t *)buffer.bytes; |
| return true; |
| case 4: |
| scalar = *(const uint32_t *)buffer.bytes; |
| return true; |
| case 8: |
| scalar = *(const uint64_t *)buffer.bytes; |
| return true; |
| case 16: |
| case 32: |
| if (buffer.length % sizeof(uint64_t) == 0) { |
| const auto length_in_bits = buffer.length * 8; |
| const auto length_in_uint64 = buffer.length / sizeof(uint64_t); |
| scalar = |
| llvm::APInt(length_in_bits, |
| llvm::ArrayRef<uint64_t>((const uint64_t *)buffer.bytes, |
| length_in_uint64)); |
| return true; |
| } |
| break; |
| } |
| } break; |
| case eTypeUInt8: |
| case eTypeUInt16: |
| case eTypeUInt32: |
| case eTypeUInt64: |
| case eTypeUInt128: |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| scalar = m_scalar; |
| return true; |
| } |
| return false; |
| } |
| |
| void RegisterValue::Clear() { m_type = eTypeInvalid; } |
| |
| RegisterValue::Type RegisterValue::SetType(const RegisterInfo *reg_info) { |
| // To change the type, we simply copy the data in again, using the new format |
| RegisterValue copy; |
| DataExtractor copy_data; |
| if (copy.CopyValue(*this) && copy.GetData(copy_data)) |
| SetValueFromData(reg_info, copy_data, 0, true); |
| |
| return m_type; |
| } |
| |
| Status RegisterValue::SetValueFromData(const RegisterInfo *reg_info, |
| DataExtractor &src, |
| lldb::offset_t src_offset, |
| bool partial_data_ok) { |
| Status error; |
| |
| if (src.GetByteSize() == 0) { |
| error.SetErrorString("empty data."); |
| return error; |
| } |
| |
| if (reg_info->byte_size == 0) { |
| error.SetErrorString("invalid register info."); |
| return error; |
| } |
| |
| uint32_t src_len = src.GetByteSize() - src_offset; |
| |
| if (!partial_data_ok && (src_len < reg_info->byte_size)) { |
| error.SetErrorString("not enough data."); |
| return error; |
| } |
| |
| // Cap the data length if there is more than enough bytes for this register |
| // value |
| if (src_len > reg_info->byte_size) |
| src_len = reg_info->byte_size; |
| |
| // Zero out the value in case we get partial data... |
| memset(buffer.bytes, 0, sizeof(buffer.bytes)); |
| |
| type128 int128; |
| |
| m_type = eTypeInvalid; |
| switch (reg_info->encoding) { |
| case eEncodingInvalid: |
| break; |
| case eEncodingUint: |
| case eEncodingSint: |
| if (reg_info->byte_size == 1) |
| SetUInt8(src.GetMaxU32(&src_offset, src_len)); |
| else if (reg_info->byte_size <= 2) |
| SetUInt16(src.GetMaxU32(&src_offset, src_len)); |
| else if (reg_info->byte_size <= 4) |
| SetUInt32(src.GetMaxU32(&src_offset, src_len)); |
| else if (reg_info->byte_size <= 8) |
| SetUInt64(src.GetMaxU64(&src_offset, src_len)); |
| else if (reg_info->byte_size <= 16) { |
| uint64_t data1 = src.GetU64(&src_offset); |
| uint64_t data2 = src.GetU64(&src_offset); |
| if (src.GetByteSize() == eByteOrderBig) { |
| int128.x[0] = data1; |
| int128.x[1] = data2; |
| } else { |
| int128.x[0] = data2; |
| int128.x[1] = data1; |
| } |
| SetUInt128(llvm::APInt(128, 2, int128.x)); |
| } |
| break; |
| case eEncodingIEEE754: |
| if (reg_info->byte_size == sizeof(float)) |
| SetFloat(src.GetFloat(&src_offset)); |
| else if (reg_info->byte_size == sizeof(double)) |
| SetDouble(src.GetDouble(&src_offset)); |
| else if (reg_info->byte_size == sizeof(long double)) |
| SetLongDouble(src.GetLongDouble(&src_offset)); |
| break; |
| case eEncodingVector: { |
| m_type = eTypeBytes; |
| buffer.length = reg_info->byte_size; |
| buffer.byte_order = src.GetByteOrder(); |
| assert(buffer.length <= kMaxRegisterByteSize); |
| if (buffer.length > kMaxRegisterByteSize) |
| buffer.length = kMaxRegisterByteSize; |
| if (src.CopyByteOrderedData( |
| src_offset, // offset within "src" to start extracting data |
| src_len, // src length |
| buffer.bytes, // dst buffer |
| buffer.length, // dst length |
| buffer.byte_order) == 0) // dst byte order |
| { |
| error.SetErrorStringWithFormat( |
| "failed to copy data for register write of %s", reg_info->name); |
| return error; |
| } |
| } |
| } |
| |
| if (m_type == eTypeInvalid) |
| error.SetErrorStringWithFormat( |
| "invalid register value type for register %s", reg_info->name); |
| return error; |
| } |
| |
| // Helper function for RegisterValue::SetValueFromString() |
| static bool ParseVectorEncoding(const RegisterInfo *reg_info, |
| llvm::StringRef vector_str, |
| const uint32_t byte_size, |
| RegisterValue *reg_value) { |
| // Example: vector_str = "{0x2c 0x4b 0x2a 0x3e 0xd0 0x4f 0x2a 0x3e 0xac 0x4a |
| // 0x2a 0x3e 0x84 0x4f 0x2a 0x3e}". |
| vector_str = vector_str.trim(); |
| vector_str.consume_front("{"); |
| vector_str.consume_back("}"); |
| vector_str = vector_str.trim(); |
| |
| char Sep = ' '; |
| |
| // The first split should give us: |
| // ('0x2c', '0x4b 0x2a 0x3e 0xd0 0x4f 0x2a 0x3e 0xac 0x4a 0x2a 0x3e 0x84 0x4f |
| // 0x2a 0x3e'). |
| llvm::StringRef car; |
| llvm::StringRef cdr = vector_str; |
| std::tie(car, cdr) = vector_str.split(Sep); |
| std::vector<uint8_t> bytes; |
| unsigned byte = 0; |
| |
| // Using radix auto-sensing by passing 0 as the radix. Keep on processing the |
| // vector elements as long as the parsing succeeds and the vector size is < |
| // byte_size. |
| while (!car.getAsInteger(0, byte) && bytes.size() < byte_size) { |
| bytes.push_back(byte); |
| std::tie(car, cdr) = cdr.split(Sep); |
| } |
| |
| // Check for vector of exact byte_size elements. |
| if (bytes.size() != byte_size) |
| return false; |
| |
| reg_value->SetBytes(&(bytes.front()), byte_size, eByteOrderLittle); |
| return true; |
| } |
| |
| Status RegisterValue::SetValueFromString(const RegisterInfo *reg_info, |
| llvm::StringRef value_str) { |
| Status error; |
| if (reg_info == nullptr) { |
| error.SetErrorString("Invalid register info argument."); |
| return error; |
| } |
| |
| m_type = eTypeInvalid; |
| if (value_str.empty()) { |
| error.SetErrorString("Invalid c-string value string."); |
| return error; |
| } |
| const uint32_t byte_size = reg_info->byte_size; |
| |
| uint64_t uval64; |
| int64_t ival64; |
| float flt_val; |
| double dbl_val; |
| long double ldbl_val; |
| switch (reg_info->encoding) { |
| case eEncodingInvalid: |
| error.SetErrorString("Invalid encoding."); |
| break; |
| |
| case eEncodingUint: |
| if (byte_size > sizeof(uint64_t)) { |
| error.SetErrorStringWithFormat( |
| "unsupported unsigned integer byte size: %u", byte_size); |
| break; |
| } |
| if (value_str.getAsInteger(0, uval64)) { |
| error.SetErrorStringWithFormat( |
| "'%s' is not a valid unsigned integer string value", |
| value_str.str().c_str()); |
| break; |
| } |
| |
| if (!Args::UInt64ValueIsValidForByteSize(uval64, byte_size)) { |
| error.SetErrorStringWithFormat( |
| "value 0x%" PRIx64 |
| " is too large to fit in a %u byte unsigned integer value", |
| uval64, byte_size); |
| break; |
| } |
| |
| if (!SetUInt(uval64, reg_info->byte_size)) { |
| error.SetErrorStringWithFormat( |
| "unsupported unsigned integer byte size: %u", byte_size); |
| break; |
| } |
| // TODO: Shouldn't we be setting m_type here? |
| break; |
| |
| case eEncodingSint: |
| if (byte_size > sizeof(long long)) { |
| error.SetErrorStringWithFormat("unsupported signed integer byte size: %u", |
| byte_size); |
| break; |
| } |
| |
| if (value_str.getAsInteger(0, ival64)) { |
| error.SetErrorStringWithFormat( |
| "'%s' is not a valid signed integer string value", |
| value_str.str().c_str()); |
| break; |
| } |
| |
| if (!Args::SInt64ValueIsValidForByteSize(ival64, byte_size)) { |
| error.SetErrorStringWithFormat( |
| "value 0x%" PRIx64 |
| " is too large to fit in a %u byte signed integer value", |
| ival64, byte_size); |
| break; |
| } |
| |
| if (!SetUInt(ival64, reg_info->byte_size)) { |
| error.SetErrorStringWithFormat("unsupported signed integer byte size: %u", |
| byte_size); |
| break; |
| } |
| |
| // TODO: Shouldn't we be setting m_type here? |
| break; |
| |
| case eEncodingIEEE754: { |
| std::string value_string = value_str; |
| if (byte_size == sizeof(float)) { |
| if (::sscanf(value_string.c_str(), "%f", &flt_val) != 1) { |
| error.SetErrorStringWithFormat("'%s' is not a valid float string value", |
| value_string.c_str()); |
| break; |
| } |
| m_scalar = flt_val; |
| m_type = eTypeFloat; |
| } else if (byte_size == sizeof(double)) { |
| if (::sscanf(value_string.c_str(), "%lf", &dbl_val) != 1) { |
| error.SetErrorStringWithFormat("'%s' is not a valid float string value", |
| value_string.c_str()); |
| break; |
| } |
| m_scalar = dbl_val; |
| m_type = eTypeDouble; |
| } else if (byte_size == sizeof(long double)) { |
| if (::sscanf(value_string.c_str(), "%Lf", &ldbl_val) != 1) { |
| error.SetErrorStringWithFormat("'%s' is not a valid float string value", |
| value_string.c_str()); |
| break; |
| } |
| m_scalar = ldbl_val; |
| m_type = eTypeLongDouble; |
| } else { |
| error.SetErrorStringWithFormat("unsupported float byte size: %u", |
| byte_size); |
| return error; |
| } |
| break; |
| } |
| case eEncodingVector: |
| if (!ParseVectorEncoding(reg_info, value_str, byte_size, this)) |
| error.SetErrorString("unrecognized vector encoding string value."); |
| break; |
| } |
| |
| return error; |
| } |
| |
| bool RegisterValue::SignExtend(uint32_t sign_bitpos) { |
| switch (m_type) { |
| case eTypeInvalid: |
| break; |
| |
| case eTypeUInt8: |
| case eTypeUInt16: |
| case eTypeUInt32: |
| case eTypeUInt64: |
| case eTypeUInt128: |
| return m_scalar.SignExtend(sign_bitpos); |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| case eTypeBytes: |
| break; |
| } |
| return false; |
| } |
| |
| bool RegisterValue::CopyValue(const RegisterValue &rhs) { |
| if (this == &rhs) |
| return rhs.m_type == eTypeInvalid ? false : true; |
| |
| m_type = rhs.m_type; |
| switch (m_type) { |
| case eTypeInvalid: |
| return false; |
| case eTypeUInt8: |
| case eTypeUInt16: |
| case eTypeUInt32: |
| case eTypeUInt64: |
| case eTypeUInt128: |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| m_scalar = rhs.m_scalar; |
| break; |
| case eTypeBytes: |
| assert(rhs.buffer.length <= kMaxRegisterByteSize); |
| ::memcpy(buffer.bytes, rhs.buffer.bytes, kMaxRegisterByteSize); |
| buffer.length = rhs.buffer.length; |
| buffer.byte_order = rhs.buffer.byte_order; |
| break; |
| } |
| return true; |
| } |
| |
| uint16_t RegisterValue::GetAsUInt16(uint16_t fail_value, |
| bool *success_ptr) const { |
| if (success_ptr) |
| *success_ptr = true; |
| |
| switch (m_type) { |
| default: |
| break; |
| case eTypeUInt8: |
| case eTypeUInt16: |
| return m_scalar.UShort(fail_value); |
| case eTypeBytes: { |
| switch (buffer.length) { |
| default: |
| break; |
| case 1: |
| case 2: |
| return *(const uint16_t *)buffer.bytes; |
| } |
| } break; |
| } |
| if (success_ptr) |
| *success_ptr = false; |
| return fail_value; |
| } |
| |
| uint32_t RegisterValue::GetAsUInt32(uint32_t fail_value, |
| bool *success_ptr) const { |
| if (success_ptr) |
| *success_ptr = true; |
| switch (m_type) { |
| default: |
| break; |
| case eTypeUInt8: |
| case eTypeUInt16: |
| case eTypeUInt32: |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| return m_scalar.UInt(fail_value); |
| case eTypeBytes: { |
| switch (buffer.length) { |
| default: |
| break; |
| case 1: |
| case 2: |
| case 4: |
| return *(const uint32_t *)buffer.bytes; |
| } |
| } break; |
| } |
| if (success_ptr) |
| *success_ptr = false; |
| return fail_value; |
| } |
| |
| uint64_t RegisterValue::GetAsUInt64(uint64_t fail_value, |
| bool *success_ptr) const { |
| if (success_ptr) |
| *success_ptr = true; |
| switch (m_type) { |
| default: |
| break; |
| case eTypeUInt8: |
| case eTypeUInt16: |
| case eTypeUInt32: |
| case eTypeUInt64: |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| return m_scalar.ULongLong(fail_value); |
| case eTypeBytes: { |
| switch (buffer.length) { |
| default: |
| break; |
| case 1: |
| return *(const uint8_t *)buffer.bytes; |
| case 2: |
| return *(const uint16_t *)buffer.bytes; |
| case 4: |
| return *(const uint32_t *)buffer.bytes; |
| case 8: |
| return *(const uint64_t *)buffer.bytes; |
| } |
| } break; |
| } |
| if (success_ptr) |
| *success_ptr = false; |
| return fail_value; |
| } |
| |
| llvm::APInt RegisterValue::GetAsUInt128(const llvm::APInt &fail_value, |
| bool *success_ptr) const { |
| if (success_ptr) |
| *success_ptr = true; |
| switch (m_type) { |
| default: |
| break; |
| case eTypeUInt8: |
| case eTypeUInt16: |
| case eTypeUInt32: |
| case eTypeUInt64: |
| case eTypeUInt128: |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| return m_scalar.UInt128(fail_value); |
| case eTypeBytes: { |
| switch (buffer.length) { |
| default: |
| break; |
| case 1: |
| case 2: |
| case 4: |
| case 8: |
| case 16: |
| return llvm::APInt(BITWIDTH_INT128, NUM_OF_WORDS_INT128, |
| ((const type128 *)buffer.bytes)->x); |
| } |
| } break; |
| } |
| if (success_ptr) |
| *success_ptr = false; |
| return fail_value; |
| } |
| |
| float RegisterValue::GetAsFloat(float fail_value, bool *success_ptr) const { |
| if (success_ptr) |
| *success_ptr = true; |
| switch (m_type) { |
| default: |
| break; |
| case eTypeUInt32: |
| case eTypeUInt64: |
| case eTypeUInt128: |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| return m_scalar.Float(fail_value); |
| } |
| if (success_ptr) |
| *success_ptr = false; |
| return fail_value; |
| } |
| |
| double RegisterValue::GetAsDouble(double fail_value, bool *success_ptr) const { |
| if (success_ptr) |
| *success_ptr = true; |
| switch (m_type) { |
| default: |
| break; |
| |
| case eTypeUInt32: |
| case eTypeUInt64: |
| case eTypeUInt128: |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| return m_scalar.Double(fail_value); |
| } |
| if (success_ptr) |
| *success_ptr = false; |
| return fail_value; |
| } |
| |
| long double RegisterValue::GetAsLongDouble(long double fail_value, |
| bool *success_ptr) const { |
| if (success_ptr) |
| *success_ptr = true; |
| switch (m_type) { |
| default: |
| break; |
| |
| case eTypeUInt32: |
| case eTypeUInt64: |
| case eTypeUInt128: |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| return m_scalar.LongDouble(); |
| } |
| if (success_ptr) |
| *success_ptr = false; |
| return fail_value; |
| } |
| |
| const void *RegisterValue::GetBytes() const { |
| switch (m_type) { |
| case eTypeInvalid: |
| break; |
| case eTypeUInt8: |
| case eTypeUInt16: |
| case eTypeUInt32: |
| case eTypeUInt64: |
| case eTypeUInt128: |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| return m_scalar.GetBytes(); |
| case eTypeBytes: |
| return buffer.bytes; |
| } |
| return nullptr; |
| } |
| |
| uint32_t RegisterValue::GetByteSize() const { |
| switch (m_type) { |
| case eTypeInvalid: |
| break; |
| case eTypeUInt8: |
| return 1; |
| case eTypeUInt16: |
| return 2; |
| case eTypeUInt32: |
| case eTypeUInt64: |
| case eTypeUInt128: |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| return m_scalar.GetByteSize(); |
| case eTypeBytes: |
| return buffer.length; |
| } |
| return 0; |
| } |
| |
| bool RegisterValue::SetUInt(uint64_t uint, uint32_t byte_size) { |
| if (byte_size == 0) { |
| SetUInt64(uint); |
| } else if (byte_size == 1) { |
| SetUInt8(uint); |
| } else if (byte_size <= 2) { |
| SetUInt16(uint); |
| } else if (byte_size <= 4) { |
| SetUInt32(uint); |
| } else if (byte_size <= 8) { |
| SetUInt64(uint); |
| } else if (byte_size <= 16) { |
| SetUInt128(llvm::APInt(128, uint)); |
| } else |
| return false; |
| return true; |
| } |
| |
| void RegisterValue::SetBytes(const void *bytes, size_t length, |
| lldb::ByteOrder byte_order) { |
| // If this assertion fires off we need to increase the size of buffer.bytes, |
| // or make it something that is allocated on the heap. Since the data buffer |
| // is in a union, we can't make it a collection class like SmallVector... |
| if (bytes && length > 0) { |
| assert(length <= sizeof(buffer.bytes) && |
| "Storing too many bytes in a RegisterValue."); |
| m_type = eTypeBytes; |
| buffer.length = length; |
| memcpy(buffer.bytes, bytes, length); |
| buffer.byte_order = byte_order; |
| } else { |
| m_type = eTypeInvalid; |
| buffer.length = 0; |
| } |
| } |
| |
| bool RegisterValue::operator==(const RegisterValue &rhs) const { |
| if (m_type == rhs.m_type) { |
| switch (m_type) { |
| case eTypeInvalid: |
| return true; |
| case eTypeUInt8: |
| case eTypeUInt16: |
| case eTypeUInt32: |
| case eTypeUInt64: |
| case eTypeUInt128: |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| return m_scalar == rhs.m_scalar; |
| case eTypeBytes: |
| if (buffer.length != rhs.buffer.length) |
| return false; |
| else { |
| uint8_t length = buffer.length; |
| if (length > kMaxRegisterByteSize) |
| length = kMaxRegisterByteSize; |
| return memcmp(buffer.bytes, rhs.buffer.bytes, length) == 0; |
| } |
| break; |
| } |
| } |
| return false; |
| } |
| |
| bool RegisterValue::operator!=(const RegisterValue &rhs) const { |
| if (m_type != rhs.m_type) |
| return true; |
| switch (m_type) { |
| case eTypeInvalid: |
| return false; |
| case eTypeUInt8: |
| case eTypeUInt16: |
| case eTypeUInt32: |
| case eTypeUInt64: |
| case eTypeUInt128: |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| return m_scalar != rhs.m_scalar; |
| case eTypeBytes: |
| if (buffer.length != rhs.buffer.length) { |
| return true; |
| } else { |
| uint8_t length = buffer.length; |
| if (length > kMaxRegisterByteSize) |
| length = kMaxRegisterByteSize; |
| return memcmp(buffer.bytes, rhs.buffer.bytes, length) != 0; |
| } |
| break; |
| } |
| return true; |
| } |
| |
| bool RegisterValue::ClearBit(uint32_t bit) { |
| switch (m_type) { |
| case eTypeInvalid: |
| break; |
| |
| case eTypeUInt8: |
| case eTypeUInt16: |
| case eTypeUInt32: |
| case eTypeUInt64: |
| case eTypeUInt128: |
| if (bit < (GetByteSize() * 8)) { |
| return m_scalar.ClearBit(bit); |
| } |
| break; |
| |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| break; |
| |
| case eTypeBytes: |
| if (buffer.byte_order == eByteOrderBig || |
| buffer.byte_order == eByteOrderLittle) { |
| uint32_t byte_idx; |
| if (buffer.byte_order == eByteOrderBig) |
| byte_idx = buffer.length - (bit / 8) - 1; |
| else |
| byte_idx = bit / 8; |
| |
| const uint32_t byte_bit = bit % 8; |
| if (byte_idx < buffer.length) { |
| buffer.bytes[byte_idx] &= ~(1u << byte_bit); |
| return true; |
| } |
| } |
| break; |
| } |
| return false; |
| } |
| |
| bool RegisterValue::SetBit(uint32_t bit) { |
| switch (m_type) { |
| case eTypeInvalid: |
| break; |
| |
| case eTypeUInt8: |
| case eTypeUInt16: |
| case eTypeUInt32: |
| case eTypeUInt64: |
| case eTypeUInt128: |
| if (bit < (GetByteSize() * 8)) { |
| return m_scalar.SetBit(bit); |
| } |
| break; |
| |
| case eTypeFloat: |
| case eTypeDouble: |
| case eTypeLongDouble: |
| break; |
| |
| case eTypeBytes: |
| if (buffer.byte_order == eByteOrderBig || |
| buffer.byte_order == eByteOrderLittle) { |
| uint32_t byte_idx; |
| if (buffer.byte_order == eByteOrderBig) |
| byte_idx = buffer.length - (bit / 8) - 1; |
| else |
| byte_idx = bit / 8; |
| |
| const uint32_t byte_bit = bit % 8; |
| if (byte_idx < buffer.length) { |
| buffer.bytes[byte_idx] |= (1u << byte_bit); |
| return true; |
| } |
| } |
| break; |
| } |
| return false; |
| } |