/* ###
 * IP: GHIDRA
 *
 * 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
 *
 *      http://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 "marshal.hh"
#include "translate.hh"

namespace ghidra {

using namespace PackedFormat;

unordered_map<string,uint4> AttributeId::lookupAttributeId;

const int4 PackedDecode::BUFFER_SIZE = 1024;

const char XmlEncode::spaces[] = "\n                        ";
const int4 XmlEncode::MAX_SPACES = 24+1;

/// Access static vector of AttributeId objects that are registered during static initialization
/// The list itself is created once on the first call to this method.
/// \return a reference to the vector
vector<AttributeId *> &AttributeId::getList(void)

{
  static vector<AttributeId *> thelist;
  return thelist;
}

/// This constructor should only be invoked for static objects.  It registers the attribute for inclusion
/// in the global hashtable.
/// \param nm is the name of the attribute
/// \param i is an id to associate with the attribute
/// \param scope is an id for the scope of this attribute
AttributeId::AttributeId(const string &nm,uint4 i,int4 scope)
  : name(nm)
{
  id = i;
  if (scope == 0)
    getList().push_back(this);
}

/// Fill the hashtable mapping attribute names to their id, from registered attribute objects
void AttributeId::initialize(void)

{
  vector<AttributeId *> &thelist(getList());
  for(int4 i=0;i<thelist.size();++i) {
    AttributeId *attrib = thelist[i];
#ifdef CPUI_DEBUG
  if (lookupAttributeId.find(attrib->name) != lookupAttributeId.end())
    throw DecoderError(attrib->name + " attribute registered more than once");
#endif
    lookupAttributeId[attrib->name] = attrib->id;
  }
  thelist.clear();
  thelist.shrink_to_fit();
}

unordered_map<string,uint4> ElementId::lookupElementId;

/// Access static vector of ElementId objects that are registered during static initialization
/// The list itself is created once on the first call to this method.
/// \return a reference to the vector
vector<ElementId *> &ElementId::getList(void)

{
  static vector<ElementId *> thelist;
  return thelist;
}

/// This constructor should only be invoked for static objects.  It registers the element for inclusion
/// in the global hashtable.
/// \param nm is the name of the element
/// \param i is an id to associate with the element
/// \param scope is an id for the scope of this element
ElementId::ElementId(const string &nm,uint4 i,int4 scope)
  : name(nm)
{
  id = i;
  if (scope == 0)
    getList().push_back(this);
}

/// Fill the hashtable mapping element names to their id, from registered element objects
void ElementId::initialize(void)

{
  vector<ElementId *> &thelist(getList());
  for(int4 i=0;i<thelist.size();++i) {
    ElementId *elem = thelist[i];
#ifdef CPUI_DEBUG
  if (lookupElementId.find(elem->name) != lookupElementId.end())
    throw DecoderError(elem->name + " element registered more than once");
#endif
    lookupElementId[elem->name] = elem->id;
  }
  thelist.clear();
  thelist.shrink_to_fit();
}

XmlDecode::~XmlDecode(void)

{
  if (document != (Document *)0)
    delete document;
}

void XmlDecode::ingestStream(istream &s)

{
  document = xml_tree(s);
  rootElement = document->getRoot();
}

uint4 XmlDecode::peekElement(void)

{
  const Element *el;
  if (elStack.empty()) {
    if (rootElement == (const Element *)0)
      return 0;
    el = rootElement;
  }
  else {
    el = elStack.back();
    List::const_iterator iter = iterStack.back();
    if (iter == el->getChildren().end())
      return 0;
    el = *iter;
  }
  return ElementId::find(el->getName(),scope);
}

uint4 XmlDecode::openElement(void)

{
  const Element *el;
  if (elStack.empty()) {
    if (rootElement == (const Element *)0)
      return 0;				// Document already traversed
    el = rootElement;
   rootElement = (const Element *)0;		// Only open once
  }
  else {
    el = elStack.back();
    List::const_iterator iter = iterStack.back();
    if (iter == el->getChildren().end())
      return 0;				// Element already fully traversed
    el = *iter;
    iterStack.back() = ++iter;
  }
  elStack.push_back(el);
  iterStack.push_back(el->getChildren().begin());
  attributeIndex = -1;
  return ElementId::find(el->getName(),scope);
}

uint4 XmlDecode::openElement(const ElementId &elemId)

{
  const Element *el;
  if (elStack.empty()) {
    if (rootElement == (const Element *)0)
      throw DecoderError("Expecting <" + elemId.getName() + "> but reached end of document");
    el = rootElement;
    rootElement = (const Element *)0;		// Only open document once
  }
  else {
    el = elStack.back();
    List::const_iterator iter = iterStack.back();
    if (iter != el->getChildren().end()) {
      el = *iter;
      iterStack.back() = ++iter;
    }
    else
      throw DecoderError("Expecting <" + elemId.getName() + "> but no remaining children in current element");
  }
  if (el->getName() != elemId.getName())
    throw DecoderError("Expecting <" + elemId.getName() + "> but got <" + el->getName() + ">");
  elStack.push_back(el);
  iterStack.push_back(el->getChildren().begin());
  attributeIndex = -1;
  return elemId.getId();
}

void XmlDecode::closeElement(uint4 id)

{
#ifdef CPUI_DEBUG
  const Element *el = elStack.back();
  if (iterStack.back() != el->getChildren().end())
    throw DecoderError("Closing element <" + el->getName() + "> with additional children");
  if (ElementId::find(el->getName(), scope) != id)
    throw DecoderError("Trying to close <" + el->getName() + "> with mismatching id");
#endif
  elStack.pop_back();
  iterStack.pop_back();
  attributeIndex = 1000;	// Cannot read any additional attributes
}

void XmlDecode::closeElementSkipping(uint4 id)

{
#ifdef CPUI_DEBUG
  const Element *el = elStack.back();
  if (ElementId::find(el->getName(), scope) != id)
    throw DecoderError("Trying to close <" + el->getName() + "> with mismatching id");
#endif
  elStack.pop_back();
  iterStack.pop_back();
  attributeIndex = 1000;  // We could check that id matches current element
}

void XmlDecode::rewindAttributes(void)

{
  attributeIndex = -1;
}

uint4 XmlDecode::getNextAttributeId(void)

{
  const Element *el = elStack.back();
  int4 nextIndex = attributeIndex + 1;
  if (nextIndex < el->getNumAttributes()) {
    attributeIndex = nextIndex;
    return AttributeId::find(el->getAttributeName(attributeIndex),scope);
  }
  return 0;
}

uint4 XmlDecode::getIndexedAttributeId(const AttributeId &attribId)

{
  const Element *el = elStack.back();
  if (attributeIndex < 0 || attributeIndex >= el->getNumAttributes())
    return ATTRIB_UNKNOWN.getId();
  // For XML, the index is encoded directly in the attribute name
  const string &attribName(el->getAttributeName(attributeIndex));
  // Does the name start with desired attribute base name?
  if (0 != attribName.compare(0,attribId.getName().size(),attribId.getName()))
    return ATTRIB_UNKNOWN.getId();
  uint4 val = 0;
  istringstream s(attribName.substr(attribId.getName().size()));	// Strip off the base name
  s >> dec >> val;		// Decode the remaining decimal integer (starting at 1)
  if (val == 0)
    throw LowlevelError("Bad indexed attribute: " + attribId.getName());
  return attribId.getId() + (val-1);
}

/// \brief Find the attribute index, within the given element, for the given name
///
/// Run through the attributes of the element until we find the one matching the name,
/// or throw an exception otherwise.
/// \param el is the given element to search
/// \param attribName is the attribute name to search for
/// \return the matching attribute index
int4 XmlDecode::findMatchingAttribute(const Element *el,const string &attribName)

{
  for(int4 i=0;i<el->getNumAttributes();++i) {
    if (el->getAttributeName(i) == attribName)
      return i;
  }
  throw DecoderError("Attribute missing: " + attribName);
}

bool XmlDecode::readBool(void)

{
  const Element *el = elStack.back();
  return xml_readbool(el->getAttributeValue(attributeIndex));
}

bool XmlDecode::readBool(const AttributeId &attribId)

{
  const Element *el = elStack.back();
  if (attribId == ATTRIB_CONTENT)
    return xml_readbool(el->getContent());
  int4 index = findMatchingAttribute(el, attribId.getName());
  return xml_readbool(el->getAttributeValue(index));
}

intb XmlDecode::readSignedInteger(void)

{
  const Element *el = elStack.back();
  intb res = 0;
  istringstream s2(el->getAttributeValue(attributeIndex));
  s2.unsetf(ios::dec | ios::hex | ios::oct);
  s2 >> res;
  return res;
}

intb XmlDecode::readSignedInteger(const AttributeId &attribId)

{
  const Element *el = elStack.back();
  intb res = 0;
  if (attribId == ATTRIB_CONTENT) {
    istringstream s(el->getContent());
    s.unsetf(ios::dec | ios::hex | ios::oct);
    s >> res;
  }
  else {
    int4 index = findMatchingAttribute(el, attribId.getName());
    istringstream s(el->getAttributeValue(index));
    s.unsetf(ios::dec | ios::hex | ios::oct);
    s >> res;
  }
  return res;
}

intb XmlDecode::readSignedIntegerExpectString(const string &expect,intb expectval)

{
  const Element *el = elStack.back();
  const string &value( el->getAttributeValue(attributeIndex) );
  if (value == expect)
    return expectval;
  istringstream s2(value);
  s2.unsetf(ios::dec | ios::hex | ios::oct);
  intb res = 0;
  s2 >> res;
  return res;
}

intb XmlDecode::readSignedIntegerExpectString(const AttributeId &attribId,const string &expect,intb expectval)

{
  string value = readString(attribId);
  if (value == expect)
    return expectval;
  istringstream s2(value);
  s2.unsetf(ios::dec | ios::hex | ios::oct);
  intb res = 0;
  s2 >> res;
  return res;
}

uintb XmlDecode::readUnsignedInteger(void)

{
  const Element *el = elStack.back();
  uintb res = 0;
  istringstream s2(el->getAttributeValue(attributeIndex));
  s2.unsetf(ios::dec | ios::hex | ios::oct);
  s2 >> res;
  return res;
}

uintb XmlDecode::readUnsignedInteger(const AttributeId &attribId)

{
  const Element *el = elStack.back();
  uintb res = 0;
  if (attribId == ATTRIB_CONTENT) {
    istringstream s(el->getContent());
    s.unsetf(ios::dec | ios::hex | ios::oct);
    s >> res;
  }
  else {
    int4 index = findMatchingAttribute(el, attribId.getName());
    istringstream s(el->getAttributeValue(index));
    s.unsetf(ios::dec | ios::hex | ios::oct);
    s >> res;
  }
  return res;
}

string XmlDecode::readString(void)

{
  const Element *el = elStack.back();
  return el->getAttributeValue(attributeIndex);
}

string XmlDecode::readString(const AttributeId &attribId)

{
  const Element *el = elStack.back();
  if (attribId == ATTRIB_CONTENT)
    return el->getContent();
  int4 index = findMatchingAttribute(el, attribId.getName());
  return el->getAttributeValue(index);
}

AddrSpace *XmlDecode::readSpace(void)

{
  const Element *el = elStack.back();
  string nm = el->getAttributeValue(attributeIndex);
  AddrSpace *res = spcManager->getSpaceByName(nm);
  if (res == (AddrSpace *)0)
    throw DecoderError("Unknown address space name: "+nm);
  return res;
}

AddrSpace *XmlDecode::readSpace(const AttributeId &attribId)

{
  const Element *el = elStack.back();
  string nm;
  if (attribId == ATTRIB_CONTENT) {
    nm = el->getContent();
  }
  else {
    int4 index = findMatchingAttribute(el, attribId.getName());
    nm = el->getAttributeValue(index);
  }
  AddrSpace *res = spcManager->getSpaceByName(nm);
  if (res == (AddrSpace *)0)
    throw DecoderError("Unknown address space name: "+nm);
  return res;
}

OpCode XmlDecode::readOpcode(void)

{
  const Element *el = elStack.back();
  string nm = el->getAttributeValue(attributeIndex);
  OpCode opc = get_opcode(nm);
  if (opc == (OpCode)0)
    throw DecoderError("Bad encoded OpCode");
  return opc;
}

OpCode XmlDecode::readOpcode(AttributeId &attribId)

{
  const Element *el = elStack.back();
  string nm;
  if (attribId == ATTRIB_CONTENT) {
    nm = el->getContent();
  }
  else {
    int4 index = findMatchingAttribute(el, attribId.getName());
    nm = el->getAttributeValue(index);
  }
  OpCode opc = get_opcode(nm);
  if (opc == (OpCode)0)
    throw DecoderError("Bad encoded OpCode");
  return opc;
}

void XmlEncode::newLine(void)

{
  if (!doFormatting)
    return;

  int numSpaces = depth * 2 + 1;
  if (numSpaces > MAX_SPACES) {
    numSpaces = MAX_SPACES;
  }
  outStream.write(spaces,numSpaces);
}

void XmlEncode::openElement(const ElementId &elemId)

{
  if (tagStatus == tag_start)
    outStream << '>';
  else
    tagStatus = tag_start;
  newLine();
  outStream << '<' << elemId.getName();
  depth += 1;
}

void XmlEncode::closeElement(const ElementId &elemId)

{
  depth -= 1;
  if (tagStatus == tag_start) {
    outStream << "/>";
    tagStatus = tag_stop;
    return;
  }
  if (tagStatus != tag_content)
    newLine();
  else
    tagStatus = tag_stop;

  outStream << "</" << elemId.getName() << '>';
}

void XmlEncode::writeBool(const AttributeId &attribId,bool val)

{
  if (attribId == ATTRIB_CONTENT) {	// Special id indicating, text value
    if (tagStatus == tag_start) {
      outStream << '>';
    }
    if (val)
      outStream << "true";
    else
      outStream << "false";
    tagStatus = tag_content;
    return;
  }
  a_v_b(outStream, attribId.getName(), val);
}

void XmlEncode::writeSignedInteger(const AttributeId &attribId,intb val)

{
  if (attribId == ATTRIB_CONTENT) {	// Special id indicating, text value
    if (tagStatus == tag_start) {
      outStream << '>';
    }
    outStream << dec << val;
    tagStatus = tag_content;
    return;
  }
  a_v_i(outStream, attribId.getName(), val);
}

void XmlEncode::writeUnsignedInteger(const AttributeId &attribId,uintb val)

{
  if (attribId == ATTRIB_CONTENT) {	// Special id indicating, text value
    if (tagStatus == tag_start) {
      outStream << '>';
    }
    outStream << hex << "0x" << val;
    tagStatus = tag_content;
    return;
  }
  a_v_u(outStream, attribId.getName(), val);
}

void XmlEncode::writeString(const AttributeId &attribId,const string &val)

{
  if (attribId == ATTRIB_CONTENT) {	// Special id indicating, text value
    if (tagStatus == tag_start) {
      outStream << '>';
    }
    xml_escape(outStream, val.c_str());
    tagStatus = tag_content;
    return;
  }
  a_v(outStream,attribId.getName(),val);
}

void XmlEncode::writeStringIndexed(const AttributeId &attribId,uint4 index,const string &val)

{
  outStream << ' ' << attribId.getName() << dec << index + 1;
  outStream << "=\"";
  xml_escape(outStream,val.c_str());
  outStream << "\"";

}

void XmlEncode::writeSpace(const AttributeId &attribId,const AddrSpace *spc)

{
  if (attribId == ATTRIB_CONTENT) {	// Special id indicating, text value
    if (tagStatus == tag_start) {
      outStream << '>';
    }
    xml_escape(outStream, spc->getName().c_str());
    tagStatus = tag_content;
    return;
  }
  a_v(outStream,attribId.getName(),spc->getName());
}

void XmlEncode::writeOpcode(const AttributeId &attribId,OpCode opc)

{
  const char *name = get_opname(opc);
  if (attribId == ATTRIB_CONTENT) {	// Special id indicating, text value
    if (tagStatus == tag_start) {
      outStream << '>';
    }
    outStream << name;
    tagStatus = tag_content;
    return;
  }
  outStream << ' ' << attribId.getName() << "=\"";
  outStream << name;
  outStream << "\"";
}

/// The integer is encoded, 7-bits per byte, starting with the most significant 7-bits.
/// The integer is decode from the \e current position, and the position is advanced.
/// \param len is the number of bytes to extract
uint8 PackedDecode::readInteger(int4 len)

{
  uint8 res = 0;
  while(len > 0) {
    res <<= RAWDATA_BITSPERBYTE;
    res |= (getNextByte(curPos) & RAWDATA_MASK);
    len -= 1;
  }
  return res;
}

/// The \e current position is reset to the start of the current open element. Attributes are scanned
/// and skipped until the attribute matching the given id is found.  The \e current position is set to the
/// start of the matching attribute, in preparation for one of the read*() methods.
/// If the id is not found an exception is thrown.
/// \param attribId is the attribute id to scan for.
void PackedDecode::findMatchingAttribute(const AttributeId &attribId)

{
  curPos = startPos;
  for(;;) {
    uint1 header1 = getByte(curPos);
    if ((header1 & HEADER_MASK) != ATTRIBUTE) break;
    uint4 id = header1 & ELEMENTID_MASK;
    if ((header1 & HEADEREXTEND_MASK) != 0) {
      id <<= RAWDATA_BITSPERBYTE;
      id |= (getBytePlus1(curPos) & RAWDATA_MASK);
    }
    if (attribId.getId() == id)
      return;		// Found it
    skipAttribute();
  }
  throw DecoderError("Attribute " + attribId.getName() + " is not present");
}

/// The attribute at the \e current position is scanned enough to determine its length, and the position
/// is advanced to the following byte.
void PackedDecode::skipAttribute(void)

{
  uint1 header1 = getNextByte(curPos);	// Attribute header
  if ((header1 & HEADEREXTEND_MASK) != 0)
    getNextByte(curPos);		// Extra byte for extended id
  uint1 typeByte = getNextByte(curPos);	// Type (and length) byte
  uint1 attribType = typeByte >> TYPECODE_SHIFT;
  if (attribType == TYPECODE_BOOLEAN || attribType == TYPECODE_SPECIALSPACE)
    return;				// has no additional data
  uint4 length = readLengthCode(typeByte);	// Length of data in bytes
  if (attribType == TYPECODE_STRING) {
    length = readInteger(length);	// Read length field to get final length of string
  }
  advancePosition(curPos, length);	// Skip -length- data
}

/// This assumes the header and \b type \b byte have been read.  Decode type and length info and finish
/// skipping over the attribute so that the next call to getNextAttributeId() is on cut.
/// \param typeByte is the previously scanned type byte
void PackedDecode::skipAttributeRemaining(uint1 typeByte)

{
  uint1 attribType = typeByte >> TYPECODE_SHIFT;
  if (attribType == TYPECODE_BOOLEAN || attribType == TYPECODE_SPECIALSPACE)
    return;				// has no additional data
  uint4 length = readLengthCode(typeByte);	// Length of data in bytes
  if (attribType == TYPECODE_STRING) {
    length = readInteger(length);	// Read length field to get final length of string
  }
  advancePosition(curPos, length);	// Skip -length- data
}

/// Set decoder to beginning of the stream.  Add padding to end of the stream.
/// \param bufPos is the number of bytes used by the last input buffer
void PackedDecode::endIngest(int4 bufPos)

{
  endPos.seqIter = inStream.begin();		// Set position to beginning of stream
  if (endPos.seqIter != inStream.end()) {
    endPos.current = (*endPos.seqIter).start;
    endPos.end = (*endPos.seqIter).end;
    // Make sure there is at least one character after ingested buffer
    if (bufPos == BUFFER_SIZE) {
      // Last buffer was entirely filled
      uint1 *endbuf = new uint1[1];		// Add one more buffer
      inStream.emplace_back(endbuf,endbuf + 1);
      bufPos = 0;
    }
    uint1 *buf = inStream.back().start;
    buf[bufPos] = ELEMENT_END;
  }
}

PackedDecode::~PackedDecode(void)

{
  list<ByteChunk>::const_iterator iter;
  for(iter=inStream.begin();iter!=inStream.end();++iter) {
    delete [] (*iter).start;
  }
}

void PackedDecode::ingestStream(istream &s)

{
  int4 gcount = 0;
  while(s.peek() > 0) {
    uint1 *buf = allocateNextInputBuffer(1);
    s.get((char *)buf,BUFFER_SIZE+1,'\0');
    gcount = s.gcount();
  }
  endIngest(gcount);
}

uint4 PackedDecode::peekElement(void)

{
  uint1 header1 = getByte(endPos);
  if ((header1 & HEADER_MASK) != ELEMENT_START)
    return 0;
  uint4 id = header1 & ELEMENTID_MASK;
  if ((header1 & HEADEREXTEND_MASK) != 0) {
    id <<= RAWDATA_BITSPERBYTE;
    id |= (getBytePlus1(endPos) & RAWDATA_MASK);
  }
  return id;
}

uint4 PackedDecode::openElement(void)

{
  uint1 header1 = getByte(endPos);
  if ((header1 & HEADER_MASK) != ELEMENT_START)
    return 0;
  getNextByte(endPos);
  uint4 id = header1 & ELEMENTID_MASK;
  if ((header1 & HEADEREXTEND_MASK) != 0) {
    id <<= RAWDATA_BITSPERBYTE;
    id |= (getNextByte(endPos) & RAWDATA_MASK);
  }
  startPos = endPos;
  curPos = endPos;
  header1 = getByte(curPos);
  while((header1 & HEADER_MASK) == ATTRIBUTE) {
    skipAttribute();
    header1 = getByte(curPos);
  }
  endPos = curPos;
  curPos = startPos;
  attributeRead = true;		// "Last attribute was read" is vacuously true
  return id;
}

uint4 PackedDecode::openElement(const ElementId &elemId)

{
  uint4 id = openElement();
  if (id != elemId.getId()) {
    if (id == 0)
      throw DecoderError("Expecting <" + elemId.getName() + "> but did not scan an element");
    throw DecoderError("Expecting <" + elemId.getName() + "> but id did not match");
  }
  return id;
}

void PackedDecode::closeElement(uint4 id)

{
  uint1 header1 = getNextByte(endPos);
  if ((header1 & HEADER_MASK) != ELEMENT_END)
    throw DecoderError("Expecting element close");
  uint4 closeId = header1 & ELEMENTID_MASK;
  if ((header1 & HEADEREXTEND_MASK) != 0) {
    closeId <<= RAWDATA_BITSPERBYTE;
    closeId |= (getNextByte(endPos) & RAWDATA_MASK);
  }
  if (id != closeId)
    throw DecoderError("Did not see expected closing element");
}

void PackedDecode::closeElementSkipping(uint4 id)

{
  vector<uint4> idstack;
  idstack.push_back(id);
  do {
    uint1 header1 = getByte(endPos) & HEADER_MASK;
    if (header1 == ELEMENT_END) {
      closeElement(idstack.back());
      idstack.pop_back();
    }
    else if (header1 == ELEMENT_START) {
      idstack.push_back(openElement());
    }
    else
      throw DecoderError("Corrupt stream");
  } while(!idstack.empty());
}

void PackedDecode::rewindAttributes(void)

{
  curPos = startPos;
  attributeRead = true;
}

uint4 PackedDecode::getNextAttributeId(void)

{
  if (!attributeRead)
    skipAttribute();
  uint1 header1 = getByte(curPos);
  if ((header1 & HEADER_MASK) != ATTRIBUTE)
    return 0;
  uint4 id = header1 & ELEMENTID_MASK;
  if ((header1 & HEADEREXTEND_MASK) != 0) {
    id <<= RAWDATA_BITSPERBYTE;
    id |= (getBytePlus1(curPos) & RAWDATA_MASK);
  }
  attributeRead = false;
  return id;
}

uint4 PackedDecode::getIndexedAttributeId(const AttributeId &attribId)

{
  return ATTRIB_UNKNOWN.getId();	// PackedDecode never needs to reinterpret an attribute
}

bool PackedDecode::readBool(void)

{
  uint1 header1 = getNextByte(curPos);
  if ((header1 & HEADEREXTEND_MASK)!=0)
    getNextByte(curPos);
  uint1 typeByte = getNextByte(curPos);
  attributeRead = true;
  if ((typeByte >> TYPECODE_SHIFT) != TYPECODE_BOOLEAN)
    throw DecoderError("Expecting boolean attribute");
  return ((typeByte & LENGTHCODE_MASK) != 0);
}

bool PackedDecode::readBool(const AttributeId &attribId)

{
  findMatchingAttribute(attribId);
  bool res = readBool();
  curPos = startPos;
  return res;
}

intb PackedDecode::readSignedInteger(void)

{
  uint1 header1 = getNextByte(curPos);
  if ((header1 & HEADEREXTEND_MASK)!=0)
    getNextByte(curPos);
  uint1 typeByte = getNextByte(curPos);
  uint4 typeCode = typeByte >> TYPECODE_SHIFT;
  intb res;
  if (typeCode == TYPECODE_SIGNEDINT_POSITIVE) {
    res = readInteger(readLengthCode(typeByte));
  }
  else if (typeCode == TYPECODE_SIGNEDINT_NEGATIVE) {
    res = readInteger(readLengthCode(typeByte));
    res = -res;
  }
  else {
    skipAttributeRemaining(typeByte);
    attributeRead = true;
    throw DecoderError("Expecting signed integer attribute");
  }
  attributeRead = true;
  return res;
}

intb PackedDecode::readSignedInteger(const AttributeId &attribId)

{
  findMatchingAttribute(attribId);
  intb res = readSignedInteger();
  curPos = startPos;
  return res;
}

intb PackedDecode::readSignedIntegerExpectString(const string &expect,intb expectval)

{
  intb res;
  Position tmpPos = curPos;
  uint1 header1 = getNextByte(tmpPos);
  if ((header1 & HEADEREXTEND_MASK)!=0)
    getNextByte(tmpPos);
  uint1 typeByte = getNextByte(tmpPos);
  uint4 typeCode = typeByte >> TYPECODE_SHIFT;
  if (typeCode == TYPECODE_STRING) {
    string val = readString();
    if (val != expect) {
      ostringstream s;
      s << "Expecting string \"" << expect << "\" but read \"" << val << "\"";
      throw DecoderError(s.str());
    }
    res = expectval;
  }
  else {
    res = readSignedInteger();
  }
  return res;
}

intb PackedDecode::readSignedIntegerExpectString(const AttributeId &attribId,const string &expect,intb expectval)

{
  findMatchingAttribute(attribId);
  intb res = readSignedIntegerExpectString(expect,expectval);
  curPos = startPos;
  return res;
}

uintb PackedDecode::readUnsignedInteger(void)

{
  uint1 header1 = getNextByte(curPos);
  if ((header1 & HEADEREXTEND_MASK)!=0)
    getNextByte(curPos);
  uint1 typeByte = getNextByte(curPos);
  uint4 typeCode = typeByte >> TYPECODE_SHIFT;
  uintb res;
  if (typeCode == TYPECODE_UNSIGNEDINT) {
    res = readInteger(readLengthCode(typeByte));
  }
  else {
    skipAttributeRemaining(typeByte);
    attributeRead = true;
    throw DecoderError("Expecting unsigned integer attribute");
  }
  attributeRead = true;
  return res;
}

uintb PackedDecode::readUnsignedInteger(const AttributeId &attribId)

{
  findMatchingAttribute(attribId);
  uintb res = readUnsignedInteger();
  curPos = startPos;
  return res;
}

string PackedDecode::readString(void)

{
  uint1 header1 = getNextByte(curPos);
  if ((header1 & HEADEREXTEND_MASK)!=0)
    getNextByte(curPos);
  uint1 typeByte = getNextByte(curPos);
  uint4 typeCode = typeByte >> TYPECODE_SHIFT;
  if (typeCode != TYPECODE_STRING) {
    skipAttributeRemaining(typeByte);
    attributeRead = true;
    throw DecoderError("Expecting string attribute");
  }
  int4 length = readLengthCode(typeByte);
  length = readInteger(length);

  attributeRead = true;
  int4 curLen = curPos.end - curPos.current;
  if (curLen >= length) {
    string res((const char *)curPos.current,length);
    advancePosition(curPos, length);
    return res;
  }
  string res((const char *)curPos.current,curLen);
  length -= curLen;
  advancePosition(curPos, curLen);
  while(length > 0) {
    curLen = curPos.end - curPos.current;
    if (curLen > length)
      curLen = length;
    res.append((const char *)curPos.current,curLen);
    length -= curLen;
    advancePosition(curPos, curLen);
  }
  return res;
}

string PackedDecode::readString(const AttributeId &attribId)

{
  findMatchingAttribute(attribId);
  string res = readString();
  curPos = startPos;
  return res;
}

AddrSpace *PackedDecode::readSpace(void)

{
  uint1 header1 = getNextByte(curPos);
  if ((header1 & HEADEREXTEND_MASK)!=0)
    getNextByte(curPos);
  uint1 typeByte = getNextByte(curPos);
  uint4 typeCode = typeByte >> TYPECODE_SHIFT;
  int4 res;
  AddrSpace *spc;
  if (typeCode == TYPECODE_ADDRESSSPACE) {
    res = readInteger(readLengthCode(typeByte));
    spc = spcManager->getSpace(res);
    if (spc == (AddrSpace *)0)
      throw DecoderError("Unknown address space index");
  }
  else if (typeCode == TYPECODE_SPECIALSPACE) {
    uint4 specialCode = readLengthCode(typeByte);
    if (specialCode == SPECIALSPACE_STACK)
      spc = spcManager->getStackSpace();
    else if (specialCode == SPECIALSPACE_JOIN) {
      spc = spcManager->getJoinSpace();
    }
    else {
      throw DecoderError("Cannot marshal special address space");
    }
  }
  else {
    skipAttributeRemaining(typeByte);
    attributeRead = true;
    throw DecoderError("Expecting space attribute");
  }
  attributeRead = true;
  return spc;
}

AddrSpace *PackedDecode::readSpace(const AttributeId &attribId)

{
  findMatchingAttribute(attribId);
  AddrSpace *res = readSpace();
  curPos = startPos;
  return res;
}

OpCode PackedDecode::readOpcode(void)

{
  int4 val = (int4)readSignedInteger();
  if (val < 0 || val >= CPUI_MAX)
    throw DecoderError("Bad encoded OpCode");
  return (OpCode)val;
}

OpCode PackedDecode::readOpcode(AttributeId &attribId)

{
  findMatchingAttribute(attribId);
  OpCode opc = readOpcode();
  curPos = startPos;
  return opc;
}

/// The value is either an unsigned integer, an address space index, or (the absolute value of) a signed integer.
/// A type header is passed in with the particular type code for the value already filled in.
/// This method then fills in the length code, outputs the full type header and the encoded bytes of the integer.
/// \param typeByte is the type header
/// \param val is the integer value
void PackedEncode::writeInteger(uint1 typeByte,uint8 val)

{
  uint1 lenCode;
  int4 sa;
  if (val == 0) {
    lenCode = 0;
    sa = -1;
  }
  else if (val < 0x800000000) {
    if (val < 0x200000) {
      if (val < 0x80) {
	lenCode = 1;		// 7-bits
	sa = 0;
      }
      else if (val < 0x4000) {
	lenCode = 2;		// 14-bits
	sa = RAWDATA_BITSPERBYTE;
      }
      else {
	lenCode = 3;		// 21-bits
	sa = 2*RAWDATA_BITSPERBYTE;
      }
    }
    else if (val < 0x10000000) {
      lenCode = 4;		// 28-bits
      sa = 3*RAWDATA_BITSPERBYTE;
    }
    else {
      lenCode = 5;		// 35-bits
      sa = 4*RAWDATA_BITSPERBYTE;
    }
  }
  else if (val < 0x2000000000000) {
    if (val < 0x40000000000) {
      lenCode = 6;
      sa = 5*RAWDATA_BITSPERBYTE;
    }
    else {
      lenCode = 7;
      sa = 6*RAWDATA_BITSPERBYTE;
    }
  }
  else {
    if (val < 0x100000000000000) {
      lenCode = 8;
      sa = 7*RAWDATA_BITSPERBYTE;
    }
    else if (val < 0x8000000000000000) {
      lenCode = 9;
      sa = 8*RAWDATA_BITSPERBYTE;
    }
    else {
      lenCode = 10;
      sa = 9*RAWDATA_BITSPERBYTE;
    }
  }
  typeByte |= lenCode;
  outStream.put(typeByte);
  for(;sa >= 0;sa -= RAWDATA_BITSPERBYTE) {
    uint1 piece = (val >> sa) & RAWDATA_MASK;
    piece |= RAWDATA_MARKER;
    outStream.put(piece);
  }
}

void PackedEncode::openElement(const ElementId &elemId)

{
  writeHeader(ELEMENT_START, elemId.getId());
}

void PackedEncode::closeElement(const ElementId &elemId)

{
  writeHeader(ELEMENT_END, elemId.getId());
}

void PackedEncode::writeBool(const AttributeId &attribId,bool val)

{
  writeHeader(ATTRIBUTE, attribId.getId());
  uint1 typeByte = val ? ((TYPECODE_BOOLEAN << TYPECODE_SHIFT) | 1) : (TYPECODE_BOOLEAN << TYPECODE_SHIFT);
  outStream.put(typeByte);
}

void PackedEncode::writeSignedInteger(const AttributeId &attribId,intb val)

{
  writeHeader(ATTRIBUTE, attribId.getId());
  uint1 typeByte;
  uint8 num;
  if (val < 0) {
    typeByte = (TYPECODE_SIGNEDINT_NEGATIVE << TYPECODE_SHIFT);
    num = -val;
  }
  else {
    typeByte = (TYPECODE_SIGNEDINT_POSITIVE << TYPECODE_SHIFT);
    num = val;
  }
  writeInteger(typeByte, num);
}

void PackedEncode::writeUnsignedInteger(const AttributeId &attribId,uintb val)

{
  writeHeader(ATTRIBUTE, attribId.getId());
  writeInteger((TYPECODE_UNSIGNEDINT << TYPECODE_SHIFT),val);
}

void PackedEncode::writeString(const AttributeId &attribId,const string &val)

{
  uint8 length = val.length();
  writeHeader(ATTRIBUTE, attribId.getId());
  writeInteger((TYPECODE_STRING << TYPECODE_SHIFT), length);
  outStream.write(val.c_str(), length);
}

void PackedEncode::writeStringIndexed(const AttributeId &attribId,uint4 index,const string &val)

{
  uint8 length = val.length();
  writeHeader(ATTRIBUTE, attribId.getId() + index);
  writeInteger((TYPECODE_STRING << TYPECODE_SHIFT), length);
  outStream.write(val.c_str(), length);
}

void PackedEncode::writeSpace(const AttributeId &attribId,const AddrSpace *spc)

{
  writeHeader(ATTRIBUTE, attribId.getId());
  switch(spc->getType()) {
    case IPTR_FSPEC:
      outStream.put((TYPECODE_SPECIALSPACE << TYPECODE_SHIFT) | SPECIALSPACE_FSPEC);
      break;
    case IPTR_IOP:
      outStream.put((TYPECODE_SPECIALSPACE << TYPECODE_SHIFT) | SPECIALSPACE_IOP);
      break;
    case IPTR_JOIN:
      outStream.put((TYPECODE_SPECIALSPACE << TYPECODE_SHIFT) | SPECIALSPACE_JOIN);
      break;
   case IPTR_SPACEBASE:
     if (spc->isFormalStackSpace())
       outStream.put((TYPECODE_SPECIALSPACE << TYPECODE_SHIFT) | SPECIALSPACE_STACK);
     else
       outStream.put((TYPECODE_SPECIALSPACE << TYPECODE_SHIFT) | SPECIALSPACE_SPACEBASE);	// A secondary register offset space
     break;
   default:
    uint8 spcId = spc->getIndex();
    writeInteger((TYPECODE_ADDRESSSPACE << TYPECODE_SHIFT), spcId);
    break;
  }
}

void PackedEncode::writeOpcode(const AttributeId &attribId,OpCode opc)

{
  writeHeader(ATTRIBUTE, attribId.getId());
  writeInteger((TYPECODE_SIGNEDINT_POSITIVE << TYPECODE_SHIFT), opc);
}

// Common attributes.  Attributes with multiple uses
AttributeId ATTRIB_CONTENT = AttributeId("XMLcontent",1);
AttributeId ATTRIB_ALIGN = AttributeId("align",2);
AttributeId ATTRIB_BIGENDIAN = AttributeId("bigendian",3);
AttributeId ATTRIB_CONSTRUCTOR = AttributeId("constructor",4);
AttributeId ATTRIB_DESTRUCTOR = AttributeId("destructor",5);
AttributeId ATTRIB_EXTRAPOP = AttributeId("extrapop",6);
AttributeId ATTRIB_FORMAT = AttributeId("format",7);
AttributeId ATTRIB_HIDDENRETPARM = AttributeId("hiddenretparm",8);
AttributeId ATTRIB_ID = AttributeId("id",9);
AttributeId ATTRIB_INDEX = AttributeId("index",10);
AttributeId ATTRIB_INDIRECTSTORAGE = AttributeId("indirectstorage",11);
AttributeId ATTRIB_METATYPE = AttributeId("metatype",12);
AttributeId ATTRIB_MODEL = AttributeId("model",13);
AttributeId ATTRIB_NAME = AttributeId("name",14);
AttributeId ATTRIB_NAMELOCK = AttributeId("namelock",15);
AttributeId ATTRIB_OFFSET = AttributeId("offset",16);
AttributeId ATTRIB_READONLY = AttributeId("readonly",17);
AttributeId ATTRIB_REF = AttributeId("ref",18);
AttributeId ATTRIB_SIZE = AttributeId("size",19);
AttributeId ATTRIB_SPACE = AttributeId("space",20);
AttributeId ATTRIB_THISPTR = AttributeId("thisptr",21);
AttributeId ATTRIB_TYPE = AttributeId("type",22);
AttributeId ATTRIB_TYPELOCK = AttributeId("typelock",23);
AttributeId ATTRIB_VAL = AttributeId("val",24);
AttributeId ATTRIB_VALUE = AttributeId("value",25);
AttributeId ATTRIB_WORDSIZE = AttributeId("wordsize",26);
AttributeId ATTRIB_STORAGE = AttributeId("storage",149);
AttributeId ATTRIB_STACKSPILL = AttributeId("stackspill",150);

AttributeId ATTRIB_UNKNOWN = AttributeId("XMLunknown",152); // Number serves as next open index

ElementId ELEM_DATA = ElementId("data",1);
ElementId ELEM_INPUT = ElementId("input",2);
ElementId ELEM_OFF = ElementId("off",3);
ElementId ELEM_OUTPUT = ElementId("output",4);
ElementId ELEM_RETURNADDRESS = ElementId("returnaddress",5);
ElementId ELEM_SYMBOL = ElementId("symbol",6);
ElementId ELEM_TARGET = ElementId("target",7);
ElementId ELEM_VAL = ElementId("val",8);
ElementId ELEM_VALUE = ElementId("value",9);
ElementId ELEM_VOID = ElementId("void",10);

ElementId ELEM_UNKNOWN = ElementId("XMLunknown",288); // Number serves as next open index

} // End namespace ghidra
